【问题标题】:Fallback variadic constructor - why does this work?后备可变参数构造函数 - 为什么会这样?
【发布时间】:2015-10-07 16:32:33
【问题描述】:

在回答this question 关于尝试构造一个可变参数转发引用构造函数时,只有在没有其他构造函数有效时才应调用该构造函数。也就是说,如果有一个:

C(const char*, size_t) { }                     // 1
template <typename... T, ???> C(T&&... ) { }   // 2

尽管需要转换,但我们希望 C c1{"abc", 2}; 调用 (1),但 C c2{1, 2, 3}; 调用 (2),因为 (1) 不适用。

我提出了以下解决方案:

template <typename... T,
          typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value>
           >
C(T&&... ) { }

通过提议,我的意思是,我尝试了它,并惊讶地发现它确实有效。它编译并完成了我对 gcc 和 clang 的期望。但是,我无法解释 为什么 它可以工作,或者即使它实际上 supposed 可以工作,而 gcc 和 clang 都只是特别适应。是吗?为什么?

【问题讨论】:

  • 根据我的理解,如果另一个构造函数可用于C c1{"abc", 2}; 而不是std::enable_if_t 将没有任何价值,因此创建类似template &lt;typename... T, typename = &gt; 的东西,SFINAE 不会创建模板化构造函数。
  • 重新发表我对您的回答的评论:this modified example 表明默认参数 SFINAE 考虑了 C 的 所有 构造函数,即使是那些在构造函数模板之后声明的构造函数。
  • 尽管它的价值不大,MSVC 2013 也接受 /W4 下的这种语法,并使用this example 打印与 gcc 和 clang 相同的内容,尽管它确实警告多个默认构造函数,并且智能感知似乎不认为对Foo::Foo 的第三次调用是有效的。 (同样,尽管 VC++ 18 很好用)
  • 当前的工作草案没有描述默认模板参数何时被实例化(见CWG 2008),所以我真的不知道如何能令人满意地回答这个问题。
  • 这不是is_constructible + 默认函数/模板参数的唯一问题。参见,例如,LWG 2452。

标签: c++ templates c++14


【解决方案1】:

您的代码的问题在于,我们只是在 它得到错误答案的上下文中实例化了 is_constructible。模板代码中的任何类型的缓存都可能导致错误——在调用构造函数后尝试在相同的参数上打印is_constructible!很可能会出错。

Live example 怎么会出错。请注意,它声称 C 不能从 int&amp; 构造,尽管在前一行已经这样做了。

struct C {
  C(const char*, size_t) {}
  template <class... Ts,
    typename = std::enable_if_t<!std::is_constructible<C, Ts&&...>::value>
  >
  C(Ts&&... ) { }
};

int main() {
  int a = 0;
  C x{a};
  std::cout << std::is_constructible<C, int&>{} << '\n';
}

哎呀。

我怀疑这可能违反了 ODR —— is_constructible 的两个定义在不同的位置有不同的类型?也可能不是。

Solution to the original problem that does not have this issue 也发布了。

【讨论】:

  • @dyp 可能。我只是指出提出的解决方案实际上并不起作用。我想我也解决了原来的问题。 ;) 好的,把它分成两部分。
  • 标准在哪里指定模板默认参数的实例化点? T.C.似乎在 OP 的 cmets 中暗示这是未指定的..
  • @dyp 当然,我可以相信很多东西在标准中都没有明确规定。所以要么它不起作用(因为它实例化得足够晚才能看到它自己?),要么它起作用(并且打破了世界,因为它得到了错误的答案)?我的观点是,依靠is_constructible 得到错误答案不是解决方案,而是隐藏在解决方案中的问题。
  • 不确定我是否理解您的意思:is_constructible 的工作方式与 Barry 的预期一致;它似乎实例化较晚(得到正确答案),但不会通过重新实例化它所属的构造函数模板来导致无限递归。我不确定是否允许它得到错误的答案,也不确定是否允许由于无限递归而中断(但我猜两者都未指定)。
  • “我可以从每组我无法构造的类型中构造出来”简直是自相矛盾的。我绝对可以看到它被制作成格式不正确的 NDR。
猜你喜欢
  • 2015-02-10
  • 1970-01-01
  • 2019-09-04
  • 2018-01-29
  • 1970-01-01
  • 2015-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多