简体   繁体   English

boost :: proto :: is_aggregate在聚合类型时返回false

[英]boost::proto::is_aggregate returning false when it is an aggregate type

While testing out aggregate types I tried using boost::proto::is_aggregate in order to check if the types that I'm creating are truly aggregate. 在测试聚合类型时,我尝试使用boost :: proto :: is_aggregate来检查我正在创建的类型是否真正聚合。 I wrote this code: 我写了这段代码:

#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;
}

And I expected the output to be true since aggregate types can define a copy-assignment operator (according to this: What are Aggregates and PODs and how/why are they special? ) 我期望输出为真,因为聚合类型可以定义一个复制赋值运算符(根据这个: 什么是聚合和POD以及它们如何/为什么特殊?

But the output is false. 但输出是错误的。

I also used the aggregate classes inside of the previous answer which should have returned true but instead returned false. 我还使用了上一个答案中的聚合类,它应该返回true但返回false。

This was tested on Boost 1.5.9 with both the Intel Compiler and MSVC. 这是使用英特尔编译器和MSVC在Boost 1.5.9上测试的。

Any ideas on why this has happened? 有关为何发生这种情况的任何想法?

Proto's trait is clearly not intended for wider use. Proto的特性显然不是为了更广泛的使用。

It defaults to just call PODs aggregates and then simply lists 3 - library internal - types as aggregates explicitly. 它默认只调用POD聚合,然后简单地将3 - 库内部类型列为聚合。 The behaviour described in the documentation indicates it was there to scratch an itch (the make<> functions needed a way to know which types go with T{} and which go with T() ). 文档中描述的行为表明它是在那里划伤痒( make<>函数需要一种方式来知道哪些类型与T{}一起使用,哪些类型与T()一起使用)。


Taking a step back, you should probably reconsider your reasons for wanting this trait in the first place. 退后一步,你应该首先重新考虑你想要这个特性的原因。 You can most likely make your concept checks more specific. 您最有可能使您的概念检查更具体。

Proposals to add a trait to the standard library have been met with broadly supported rejection: 向标准库添加特征的提议已得到广泛支持的拒绝:

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. 我们在添加无用的特征如is_pod和is_literal_type时犯了一个错误,我们不应该通过添加更多无用的特征来弥补这个错误。

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?"). 在我看来,类型traits应该捕获类型的可观察属性(例如,“我可以从braced-init-list直接初始化这个类型吗?”),而不是核心语言ephemera(例如“这种类型服从文字吗?”类型规则,这是一个需要编译器诊断的拐杖?“或”将支持此类型的初始化执行聚合初始化,还是会调用构造函数?“)。

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. 此外,我认为类型应该能够在聚合和提供等效构造函数集之间切换,而不必担心某人可能正在观察差异。

I have looked at the requirements and have concluded it's hard/impossible to make a fool-proof implementation. 我已经查看了要求,并得出结论很难/不可能做出一个简单的实现。 The first requirement I was unable to address programmatically is 我无法以编程方式解决的第一个要求是

no base classes (clause 10) 没有基类(第10条)

There's no way to tell a class has no (public, empty etc.) base class. 没有办法告诉一个类没有(公共,空等)基类。

So the best thing I can come up is an approximation only: 所以我能提出的最好的事情只是近似:

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>()
        )
    >
{ };

Given the limitations, it seems to do its job fairly well, at least /a lot/ better than Proto's internal trait. 考虑到这些限制,它似乎做得相当好,至少/比Proto的内部特性好很多/更好。

CAVEAT This doesn't address the subtle changes in c++11/c++14 (related to in-class member initializers eg). CAVEAT 这并没有解决c ++ 11 / c ++ 14中的细微变化(例如与类内成员初始化程序相关)。 Also some of the traits used to compose the above approximation have known issues on various compiler versions (notably MSVC, as I remember), so do not trust this trait to be accurate across all language/library versions. 另外,用于构成上述近似的一些特征在各种编译器版本上都存在已知问题(特别是MSVC,我记得),所以不要相信这个特性在所有语言/库版本中都是准确的。

Live On Coliru 住在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 compiles this cleanly (without static asserts) on: 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM