boost::proto::is_aggregate 为聚合类型时返回false
boost::proto::is_aggregate returning false when it is an aggregate type
在测试聚合类型时,我尝试使用 boost::proto::is_aggregate 来检查我正在创建的类型是否真正聚合。我写了这段代码:
#include <iostream>
#include <boost\proto\traits.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;
}
而且我希望输出为真,因为聚合类型可以定义一个复制赋值运算符(据此:What are Aggregates and PODs and how/why are they special?)
但是输出是错误的。
我还在之前的答案中使用了聚合 类,它本应返回 true 但返回了 false。
这已在 Boost 1.5.9 上使用英特尔编译器和 MSVC 进行了测试。
关于为什么会发生这种情况有什么想法吗?
Proto 的特征显然不适合更广泛的使用。
它默认只调用 PODs 聚合,然后简单地列出 3 - 库内部 - 类型明确作为聚合。文档中描述的行为表明它是为了挠痒痒(make<>
函数需要一种方法来知道哪些类型与 T{}
一起使用,哪些与 T()
一起使用)。
退一步说,您可能应该首先重新考虑您想要此特质的原因。您很可能可以使您的概念检查更具体。
向标准库添加特征的提议遭到广泛支持的拒绝:
I have reservations about this trait. We made a mistake adding useless traits like is_pod and is_literal_type, and we shouldn't compound that mistake by adding more useless traits.
In my view, type traits should capture observable properties of types (such as, "can I direct-initialize this type from that braced-init-list?"), and not core language ephemera (such as "does this type obey the literal type rules, which are a crutch for requiring a compiler diagnostic?" or "will braced initialization on this type perform aggregate initialization or will it call a constructor?").
Also, I think that a type should be able to switch between being an aggregate and providing an equivalent constructor set without worrying that someone might be observing the difference.
我已经查看了要求并得出结论,hard/impossible 可以实现万无一失的实施。我无法以编程方式解决的第一个要求是
no base classes (clause 10)
没有办法告诉 class 没有(public、空等)基础 class。
所以我能想到的最好的办法只是一个近似值:
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 中的细微变化(与 in-class 成员相关初始化程序,例如)。此外,用于构成上述近似值的一些特征在各种编译器版本(尤其是 MSVC,我记得)上存在已知问题,因此不要相信这个特征在所有 language/library 版本中都是准确的。
#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
在测试聚合类型时,我尝试使用 boost::proto::is_aggregate 来检查我正在创建的类型是否真正聚合。我写了这段代码:
#include <iostream>
#include <boost\proto\traits.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;
}
而且我希望输出为真,因为聚合类型可以定义一个复制赋值运算符(据此:What are Aggregates and PODs and how/why are they special?)
但是输出是错误的。
我还在之前的答案中使用了聚合 类,它本应返回 true 但返回了 false。
这已在 Boost 1.5.9 上使用英特尔编译器和 MSVC 进行了测试。
关于为什么会发生这种情况有什么想法吗?
Proto 的特征显然不适合更广泛的使用。
它默认只调用 PODs 聚合,然后简单地列出 3 - 库内部 - 类型明确作为聚合。文档中描述的行为表明它是为了挠痒痒(make<>
函数需要一种方法来知道哪些类型与 T{}
一起使用,哪些与 T()
一起使用)。
退一步说,您可能应该首先重新考虑您想要此特质的原因。您很可能可以使您的概念检查更具体。
向标准库添加特征的提议遭到广泛支持的拒绝:
I have reservations about this trait. We made a mistake adding useless traits like is_pod and is_literal_type, and we shouldn't compound that mistake by adding more useless traits.
In my view, type traits should capture observable properties of types (such as, "can I direct-initialize this type from that braced-init-list?"), and not core language ephemera (such as "does this type obey the literal type rules, which are a crutch for requiring a compiler diagnostic?" or "will braced initialization on this type perform aggregate initialization or will it call a constructor?").
Also, I think that a type should be able to switch between being an aggregate and providing an equivalent constructor set without worrying that someone might be observing the difference.
我已经查看了要求并得出结论,hard/impossible 可以实现万无一失的实施。我无法以编程方式解决的第一个要求是
no base classes (clause 10)
没有办法告诉 class 没有(public、空等)基础 class。
所以我能想到的最好的办法只是一个近似值:
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 中的细微变化(与 in-class 成员相关初始化程序,例如)。此外,用于构成上述近似值的一些特征在各种编译器版本(尤其是 MSVC,我记得)上存在已知问题,因此不要相信这个特征在所有 language/library 版本中都是准确的。
#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