回退可变构造函数-为什么这样做

Fallback variadic constructor - why does this work?

本文关键字:为什么 这样做 构造函数 回退      更新时间:2023-10-16

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

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的期望。然而,我无法解释为什么可以工作,或者即使实际上应该可以工作,而gcc和clang都特别适合。是吗?为什么?

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

如何出错的一个活生生的例子。注意,它声明不能从int&构造C,尽管在前一行已经这样做了。

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的两个定义在不同的位置有不同的类型?或者不是。

没有此问题的原始问题的解决方案也已发布。