【问题标题】:why templated non const parameter constructor is preferred to given copy constructor为什么模板化的非常量参数构造函数优于给定的复制构造函数
【发布时间】:2018-08-25 20:41:59
【问题描述】:

我想让我的课程能够在std::variant 中使用。

应该工作的简单代码是:

int main()
{
    std::variant< int, A > v;

    A a(1);
    v = a;
}

我的类包含一个模板化的构造函数:

 template <typename T> A( T& );

此时麻烦开始了!构造函数绑定到来自std::variant 的调用,不再使用提供的A(const A&amp;)

出于复制和粘贴原因,此处的完整示例:

#include <iostream>
#include <variant>

class A
{
    private:
        int x;

    public:

        A( A&&) {}
        A( const A& ) {}
        A(){}
        ~A() {}

        A& operator=( const A& ) { return *this;}
        A& operator=( A&& ) {return *this;}

        template <typename T>
            A( T& t  ) 
            {
                std::cout << __PRETTY_FUNCTION__ << std::endl;
            }

        A(int _x):x{_x}{}
};

int main()
{
    std::variant< int, A > v;

    A a(1);
    v = a;
}

背景:

为什么是这里的模板? 当使用采用序列化器类型的构造函数时,问题就开始了。序列化程序可以有多种类型,取决于要序列化的文件或流。

备注:我知道缺少构造函数的功能!

【问题讨论】:

  • 关于您自己的代码的问题,“为什么要在此处使用模板?”,您从哪里获得此代码?
  • 关于“A(A&); 使用类作为 std::variant 的元素的要求”,没有这个要求。
  • −1 缺少必需的标头
  • @Cheersandhth.-Alf: 完成;)
  • 谢谢。删除了负 1。

标签: c++ c++17 variant requirements


【解决方案1】:

问题不在于std::variant。问题出在构造函数模板上,

template <typename T>
A(T& t)

这样的构造函数是有问题的,因为当参数是A 类型的非const 左值时,此构造函数优于采用const A&amp; 的复制构造函数——这通常不是预期的行为。为了防止这种情况,我们通常用 SFINAE 来约束这个构造函数:

template <typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, A>>>
A(T& t)  // or T&& t

也可以考虑将其设为explicit

我们通常不提供采用非const A&amp; 的复制构造函数,因为它们在采用const A&amp; 的复制构造函数旁边是多余的。

【讨论】:

  • 带有非 const 参数的模板化构造函数优于完全匹配的“手工制作”版本?哇!
  • @Klaus 是的,因为非 const 左值更喜欢绑定到非 const 左值引用而不是 const 左值引用。但是,模板化是在重载解决的后期考虑的。
  • @Klaus:我记得从 C++03 到 C++11 的规则略有变化。
猜你喜欢
  • 2020-01-14
  • 2011-05-24
  • 2023-03-20
  • 2015-10-06
  • 2019-09-14
  • 2016-09-12
  • 1970-01-01
  • 2020-01-23
相关资源
最近更新 更多