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 版本中都是准确的。

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