【问题标题】:Specializing constructors with requires for a possibly reference type对可能的引用类型的要求专门化构造函数
【发布时间】:2021-06-01 23:08:32
【问题描述】:

我需要制作一个可能包含引用成员的包装类模板。我定义了复制和移动构造函数,其中包装类型的引用作为参数传递。如果类型不是引用,这一切都很好。但是如果是的话,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;
}

【问题讨论】:

    标签: c++ constructor reference c++20 c++-concepts


    【解决方案1】:

    您定义的每个函数都必须以某种方式不同 - 不同的名称、不同的参数、不同的约束等。

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

    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&lt;T&amp;&gt; 提供一个特化并以这种方式拆分构造函数。

    【讨论】:

    • 哦,我明白了。所以问题是编译器无法判断是否所有这些函数都需要实例化,在这种情况下,它们都会以相同的名称结束。
    • @SU3 好吧,所有构造函数都有相同的“名称”,它们只是S。但在这种情况下,问题在于它们 correspond 实际上并不相同。
    • 感谢您的澄清。我松散地使用了“名称”一词。我的意思是,代表这些构造函数的函数的编译符号必须以相同的名称结束。
    猜你喜欢
    • 1970-01-01
    • 2015-03-12
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    • 2019-11-30
    • 2022-12-13
    相关资源
    最近更新 更多