boost::proto::is_aggregate为聚合类型时返回false
boost::proto::is_aggregate returning false when it is an aggregate type
在测试聚合类型时,我尝试使用boost::proto::is_aggregate来检查我创建的类型是否真的是聚合类型。我写了这个代码:
#include <iostream>
#include <boostprototraits.hpp>
struct IsAggregate
{
IsAggregate &operator=(IsAggregate const &rhs) {}
};
int main()
{
std::cout << std::boolalpha;
std::cout << boost::proto::is_aggregate<IsAggregate>() << std::endl;
return 0;
}
我希望输出是真的,因为聚合类型可以定义复制分配运算符(根据这个:什么是聚合和POD,它们是如何/为什么特殊的?)
但是输出是错误的。
我还在上一个答案中使用了聚合类,该答案本应返回true,但却返回false。
这是在Boost 1.5.9上用"英特尔编译器"和MSVC测试过的。
你知道为什么会发生这种事吗?
Proto的特点是显然不适合更广泛的使用。
默认情况下,它只调用POD聚合,然后简单地显式列出3库内部类型作为聚合。文档中描述的行为表明它是为了解决问题(make<>
函数需要一种方法来知道哪些类型与T{}
有关,哪些与T()
有关)。
退一步看,你可能应该重新考虑你想要这种特质的原因。你很可能会让你的概念检查更加具体。
在标准库中添加特征的提议遭到了广泛支持的拒绝:
- https://groups.google.com/a/isocpp.org/forum/#!主题/std提案/L-2YY4zbgmU
我对这个特点有所保留。我们在添加is_pod和is_literal_type等无用特征时犯了一个错误,我们不应该通过添加更多无用特征来加剧这个错误。
在我看来,类型特征应该捕获类型的可观察属性(例如,"我可以直接从支持的init列表初始化这个类型吗?"),而不是核心语言ephemera(例如"此类型是否遵守字面类型规则,这是需要编译器诊断的支柱?"或"此类型上的支持初始化会执行聚合初始化还是会调用构造函数?")。
此外,我认为一个类型应该能够在作为聚合和提供等效构造函数集之间切换,而不必担心有人会注意到差异。
我已经查看了需求,并得出结论,很难/不可能实现一个傻瓜式的实现。我无法通过编程解决的第一个要求是
无基类(第10条)
没有办法告诉一个类没有(公共、空等)基类。
因此,我能想到的最好的东西只是一个近似值:
template <typename T>
struct is_aggregate : std::integral_constant<bool,
std::is_pod<T>() or
(
std::is_trivially_constructible<T>()
and std::is_trivially_destructible<T>()
and std::is_standard_layout<T>()
and not std::is_polymorphic<T>()
)
>
{ };
考虑到这些限制,它似乎做得相当好,至少比Proto的内在特质好很多。
CAVEAT这并没有解决c++11/c++14中的细微变化(例如与类内成员初始化程序有关)。此外,用于组成上述近似值的一些特性在各种编译器版本上都存在已知问题(尤其是我记得的MSVC),因此不要相信这个特性在所有语言/库版本中都是准确的
在Coliru上直播
#include <iostream>
#include <type_traits>
#include <string>
template <typename T>
struct is_aggregate : std::integral_constant<bool,
std::is_pod<T>() or
(
std::is_trivially_constructible<T>()
and std::is_trivially_destructible<T>()
and std::is_standard_layout<T>()
and not std::is_polymorphic<T>()
)
>
{ };
namespace simple { // ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
};
static_assert(is_aggregate<X>(), "");
void foo() { X x { 42 }; (void) x; }
}
namespace usr_defined_ctor { // NOT ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
X() {}
};
static_assert(!is_aggregate<X>(), "");
//void foo() { X x { 42 }; (void) x; }
}
namespace defaulted_ctor { // ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
X() = default;
};
static_assert( is_aggregate<X>(), "");
void foo() { X x { 42 }; (void) x; }
}
namespace static_data_members { // ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
X() = default;
static const bool yeah = true;
private:
static const bool no = true;
protected:
static const std::string problem;
};
bool const X::yeah;
bool const X::no;
std::string const X::problem = "whatsoever";
static_assert( is_aggregate<X>(), "");
void foo() { X x { 42 }; (void) x; }
}
namespace private_non_static_data_members { // NOT ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { (void) oops; return *this; }
private:
bool oops;
};
static_assert(!is_aggregate<X>(), "");
//void foo() { X x { 42, true }; (void) x; }
}
namespace protected_non_static_data_members { // NOT ok
struct X {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
protected:
bool oops;
};
static_assert(!is_aggregate<X>(), "");
//void foo() { X x { 42, true }; (void) x; };
}
namespace have_base_class { // NOT ok
struct B {};
struct X : B {
int x;
X &operator=(X const &/*rhs*/) { return *this; }
};
static_assert(is_aggregate<X>(), ""); // FALSE POSITIVE: the below fails to compile
//void foo() { X x { 42 }; (void) x; };
}
int main() { }
Coliru在上干净地编译(没有静态断言)
g++ -std=c++0x -O2 -Wall -pedantic -pthread main.cpp
g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
g++ -std=c++1y -O2 -Wall -pedantic -pthread main.cpp
g++ -std=c++1z -O2 -Wall -pedantic -pthread main.cpp
clang++ -std=c++0x -O2 -Wall -pedantic -pthread main.cpp
clang++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
clang++ -std=c++1y -O2 -Wall -pedantic -pthread main.cpp
clang++ -std=c++1z -O2 -Wall -pedantic -pthread main.cpp
clang++ -stdlib=libc++ -std=c++0x -O2 -Wall -pedantic -pthread main.cpp
clang++ -stdlib=libc++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
clang++ -stdlib=libc++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
clang++ -stdlib=libc++ -std=c++1y -O2 -Wall -pedantic -pthread main.cpp
clang++ -stdlib=libc++ -std=c++1z -O2 -Wall -pedantic -pthread main.cpp
- 如何在模板参数中分离函数类型返回类型和参数
- 为什么要为指针返回类型返回一系列字符?
- 有没有办法根据运行时值的类型返回 constexpr 对象
- 从弱指针返回类型返回共享指针
- 无法为unique_ptr返回类型返回 nullptr
- 为什么双重类型返回无穷大
- std::is_arithmetic 为通用 lambda 中的 int 类型返回 false:未定义的行为?
- C 策略设计具有可变类型返回值
- 根据类型返回一个值
- 在C++中,将double转换为long的类型返回了不正确的值
- 作为宏参数的安装类型返回错误:字符串常量之前的预期非限定 id
- 具有引用类型返回的函数
- C++ 如何使模板<T>f() 为整数 T 返回 -1,为指针类型返回 nullptr
- C++模板:按类型返回值
- 以Integer返回类型返回浮点值
- 使用 C++14 的自动功能类型返回扣除代替 std::common_type 是否始终安全?
- 为什么允许新运算符向每个指针类型返回*void
- 适当的函数返回类型返回日期时间
- c++中布尔类型返回函数的默认返回值
- 在c#中使用非托管c++代码对所有double类型返回0