检查引用元组是否为默认构造时出错

use*_*888 6 c++ std c++11 stdtuple c++14

使用g ++ - 5,我得到以下输出

#include <type_traits>
#include <tuple>
int main()
{
  bool b;
  b = std::is_default_constructible<int>::value; //Compiles, returns true
  b = std::is_default_constructible<int&>::value; //Compiles, returns false
  b = std::is_default_constructible< std::tuple<int> >::value; //Compiles, returns true
  b = std::is_default_constructible< std::tuple<int&> >::value; //Does not compile
}
Run Code Online (Sandbox Code Playgroud)

这是is_default_constructible实施中的错误吗?

错误消息是一个长堆栈列表,结尾于:

/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/tuple:105:9: error: reference to type 'int' requires an initializer
  : _M_head_impl() { }
Run Code Online (Sandbox Code Playgroud)

dyp*_*dyp 7

不是一个错误is_default_constructible.该类型特征仅需要检查默认构造的直接上下文,它不必深入评估任何成员初始化器.这种限制可能是因为它可以通过使用SFINAE在没有专用编译器魔法的情况下实现.(见[meta.unary.prop],特别是p7).

tuplepair默认构造函数不要求在直接的上下文(SFINAE友好)如果元素类型不能为缺省构造失败.这已由LWG 2367解决,它为tuple默认构造函数引入了以下SFINAE要求:

备注:除非is_default_constructible<Ti>::value对所有人都适用,否则此构造函数不应参与重载决策i.[...]

有了这个额外的要求,元组的默认构造必须以SFINAE友好的方式失败,这样is_default_constructible现在适用于tuple如果元素未能在直接上下文中默认构造(对于引用类型的情况).

LWG 2367目前处于Ready状态; 提议的决议尚未(尚未)纳入github草案.


[ - 这部分仍在考虑之中

Yakk在评论中提出了一个重要观点:为什么is_default_constructible必须深度实例化成员初始化器?

据我所知,这与条件constexpr'iveness of tuple'的默认构造函数有关.is_default_constructible导致默认构造函数的实例化.它只需要实例化声明,以确定是否可以在紧急上下文中调用此构造函数而不会失败.但是,声明的实例化需要确定constexpr'iveness',这会导致构造函数定义的实例化.

已标记为的类模板的成员函数(或构造函数)constexpr仅是有条件的constexpr:只有这些类模板实例化的成员函数才会constexpr在正文不违反constexpr限制的情况下.这需要为构造函数实例化构造函数的主体,以便检查是否允许在constexpr函数内部使用成员初始值设定项.考虑:

struct nonconstexpr { nonconstexpr() { std::cout << "runtime\n"; } };
struct isconstexpr { constexpr isconstexpr() {} };

template<typename T>
struct wrapper { T t; constexpr wrapper() : t() {} };
Run Code Online (Sandbox Code Playgroud)

在实例化默认的ctor时wrapper,编译器必须实例化成员初始值设定项,以确定该实例化是否应该是constexpr.

在这种情况下std::tuple,这会导致某个成员初始化程序的实例化,它尝试对初始化引用元组叶(数据成员)进行值初始化.这是一个错误,它不会发生在默认构造函数的原始实例化的直接上下文中.因此,这是一个很难的错误,而不是直接背景下的替代失败.

- ]

这一部分对我来说并不完全清楚,因为CWG 1358基本上完成了所有实例化constexpr,无论它们是否真正符合标准.事实上,gcc 6.0并没有编译以下示例,而gcc 5.1和clang 3.7拒绝它:

#include <type_traits>

template<typename T>
struct foo
{
    T t;
    constexpr foo() {} // remove `constexpr` to make it compile everywhere
};

int main()
{
    static_assert(std::is_default_constructible<foo<int&>>{}, "!");
}
Run Code Online (Sandbox Code Playgroud)

CWG 1358还告诉我们为什么两种方法之间的区别 - 条件constexpr和constexpr尽管有违规 - 很重要:

在问题1581的讨论中出现的问题是,这种方法 - 使constexpr函数模板的特化或类模板的成员函数仍然是constexpr但是无法在常量上下文中调用 - 是正确的.这意味着类类型可能被归类为文字,但无法在编译时实例化.因此,此问题将返回"审核"状态,以便进一步考虑此问题.


对于libc ++,有一个错误#21157,已于2014-10-15解决并出现在clang3.6分支中.对于libstdc ++,似乎没有错误报告; 该问题已在2015-06-30合并提交中得到修复,该提交还实现了N4387 - 改进对和元组(修订版3),目前似乎没有出现在任何gcc5分支中.