专门化构造函数需要可能的引用类型

Specializing constructors with requires for a possibly reference type

我需要制作一个可能包含参考成员的包装器 class 模板。我定义了复制和移动构造函数,其中包装类型的引用作为参数传递。如果类型不是引用,这一切都很好。但是如果是的话,type&type&&都变成了左值引用,两个构造函数就冲突了。

我尝试为类型不是引用的情况定义一组构造函数,为类型不是引用的情况定义另一组构造函数,使用 requires 子句,如下面的代码所示。但如果类型是引用,这将无法编译! GCC 以某种方式将中毒的构造函数视为仍然有效。

我是不是误解了 requires 的工作原理,或者这是一个错误?这是编译器资源管理器中的相同示例:https://godbolt.org/z/qjMxx3nGE

#include <type_traits>

template <typename T>
struct S {
  using type = T;
  type x;
  S(const type& x) requires(!std::is_reference_v<type>)
  : x(x) { }
  S(type&& x) requires(!std::is_reference_v<type>)
  : x(x) { }
  S(type x) requires(std::is_reference_v<type>)
  : x(x) { }
};

int main(int argc, char** argv) {
  int i = 5;
  S<int&> s(i);
  return s.x;
}

您定义的每个函数都必须在某些方面有所不同 - 不同的名称、不同的参数、不同的约束等。

对于T=int&,你的三个构造函数是:

S(int&) requires (!std::is_reference_v<int&>);
S(int&) requires (!std::is_reference_v<int&>);
S(int&) requires (std::is_reference_v<int&>);

前两个相同,但有两个。那是一个错误。你需要区分这些情况。

requires 不会阻止该函数以您可能认为的方式存在 - 它只是将其作为重载决议的候选者删除。它的行为不像假设的 if 那样:

if (std::is_reference_v<T>) {
    S(T x);
} else {
    S(T const&);
    S(T&&);
}

简单地添加一个受限于可构造性的构造函数模板会更容易:

template <typename U> requires std::constructible_from<T, U>
S(U&& u) : x(std::forward<U>(u)) { }

或者为 S<T&> 提供专门化并以这种方式拆分构造函数。