Proto 的特性显然不适合更广泛的使用。
它默认只调用 POD 聚合,然后简单地列出 3 个库内部类型作为聚合显式。文档中描述的行为表明它在那里抓痒(make<>
函数需要一种方法来知道哪些类型与哪些类型一起T{}
使用T()
)。
退后一步,你可能应该重新考虑你首先想要这个特性的原因。您最有可能使您的概念检查更加具体。
向标准库添加 trait 的提议遭到了广泛支持的拒绝:
我对这个特征持保留态度。我们在添加诸如 is_pod 和 is_literal_type 之类的无用特征时犯了一个错误,我们不应该通过添加更多无用特征来加剧该错误。
在我看来,类型特征应该捕获类型的可观察属性(例如,“我可以从那个花括号初始化列表中直接初始化这个类型吗?”),而不是核心语言蜉蝣(例如“这种类型是否遵守文字类型规则,这是需要编译器诊断的关键?”或“这种类型的大括号初始化会执行聚合初始化还是会调用构造函数?”)。
此外,我认为一个类型应该能够在聚合和提供等效构造函数集之间切换,而不必担心有人可能会观察到差异。
我查看了要求并得出结论,很难/不可能进行万无一失的实施。我无法以编程方式解决的第一个要求是
没有基类(第 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,我记得)上存在已知问题,所以不要相信这个特征在所有语言/库版本中都是准确的。
Live On 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