c++ - 如何解决 "read of non-constexpr variable ' a' is not allowed in a constant expression"with boost.hana

标签 c++ boost boost-hana

我在 Boost.hana 中使用 c++17 来编写一些元编程程序。困扰我的一个问题是在像 static_assert 这样的 constexpr 上下文中可以使用什么样的表达式。下面是一个例子:

#include <boost/hana.hpp>

using namespace boost::hana::literals;

template <typename T>
class X {
public:
    T data;

    constexpr explicit X(T x) : data(x) {}

    constexpr T getData() {
        return data;
    }
};


int main() {
    {   // test1
        auto x1 = X(1_c);
        static_assert(x1.data == 1_c);
        static_assert(x1.getData() == 1_c);
    }
    {   //test2.1
        auto x2 = X(boost::hana::make_tuple(1_c, 2_c));
        static_assert(x2.data[0_c] == 1_c);

        // static_assert(x2.getData()[0_c] == 1_c); // read of non-constexpr variable 'x2' is not allowed in a constant expression
    }
    {   //test2.2
        auto x2 = X(boost::hana::make_tuple(1_c, 2_c));
        auto data = x2.getData();
        static_assert(data[0_c] == 1_c);
    }
}

首先,我编写了一个带有字段数据和访问器 getData() 的类 X。在 main() 的 test1 部分,x1.data 和 x1.getData() 的行为与我预期的一样。但是在 test2 部分,将参数更改为 boost::hana 的元组,static_assert(x2.data[0_c] == 1_c)仍然表现良好,但 static_assert(x2.getData()[0_c] == 1_c)编译失败,错误为“常量表达式中不允许读取非 constexpr 变量‘x2’”。奇怪的是,如果我拆分 x2.getData()[0_c]进入 auto data = x2.getData();static_assert(data[0_c] == 1_c);它再次编译正常。我希望他们的行为相同。那么任何人都可以帮助解释为什么x2.getData()[0_c]在这个例子中不能在 static_assert 中使用吗?

重现:clang++8.0 -I/path/to/hana-1.5.0/include -std=c++17 Test.cpp

最佳答案

问题是boost::hana::tuple没有复制构造函数。

它有 a constructor看起来像一个复制构造函数:

template <typename ...dummy, typename = typename std::enable_if<
    detail::fast_and<BOOST_HANA_TT_IS_CONSTRUCTIBLE(Xn, Xn const&, dummy...)...>::value
>::type>
constexpr tuple(tuple const& other)
    : tuple(detail::from_index_sequence_t{},
            std::make_index_sequence<sizeof...(Xn)>{},
            other.storage_)
{ }

但由于这是一个模板,所以它是 not a copy constructor .

boost::hana::tuple没有复制构造函数,一个是 declared implicitly并定义为默认值(它没有被抑制,因为 boost::hana::tuple 没有任何复制或移动构造函数或赋值运算符,因为,你猜对了,它们不能是模板)。

在这里我们看到implementation divergence ,在以下程序的行为中得到了证明:
struct A {
    struct B {} b;
    constexpr A() {};
    // constexpr A(A const& a) : b{a.b} {}    // #1
};
int main() {
    auto a = A{};
    constexpr int i = (A{a}, 0);
}

gcc 接受,而 Clang 和 MSVC 拒绝,但如果行 #1 接受未注释。也就是说,编译器不同意非(直接)空类的隐式定义的复制构造函数是否允许在常量评估上下文中使用。

根据 implicitly-defined copy constructor 的定义#1 与 constexpr A(A const&) = default; 没有任何不同所以 gcc 是正确的。还要注意,如果我们给 B 一个用户定义的 constexpr 复制构造函数 Clang 和 MSVC 再次接受,那么问题似乎是这些编译器无法跟踪递归空的隐式可复制类的 constexpr 复制构造性。为 MSVC 提交了错误和 Clang ( fixed 用于 Clang 11)。

注意使用operator[]是红鲱鱼;问题是编译器是否允许调用 getData() (复制构造 T )在常量评估上下文中,例如 static_assert .

显然,理想的解决方案是让 Boost.Hana 修正 boost::hana::tuple这样它就有实际的复制/移动构造函数和复制/移动赋值运算符。 (这将解决您的用例,因为代码将调用用户提供的复制构造函数,这在常量评估上下文中是允许的。)As a workaround ,你可以考虑破解getData()检测无状态的情况 T :
constexpr T getData() {
    if (data == T{})
        return T{};
    else
        return data;
}

关于c++ - 如何解决 "read of non-constexpr variable ' a' is not allowed in a constant expression"with boost.hana,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60387132/

相关文章:

c++ - 无法将列表迭代器转换为对象

C++ 与最低位集进行比较

c++ - BOOST_FUSION_ADAPT_STRUCT的替代实现

c++ - 如何在 boost beast websocket 中传递模型类型

c++ - 如何反转 hana::string

c++ - boost::hana::map 作为数据成员

c++ - "Error LNK2001: unresolved external symbol _main"仍然存在

c++ - boost::asio signal_set 处理程序仅在捕获到第一个信号后执行,并忽略相同类型的连续信号

c++ - 如何连接两个 Boost Hana map ?

c++ - C++ 中的未定义行为