强制编译器选择以 const T& 作为参数的复制构造函数

Force compiler to choose copy constructor with const T& as a parameter

我正在编写一个 class,其中我有一个模板化构造函数和复制构造函数。每次我想用非 const 对象调用复制构造函数时,都会选择模板化构造函数。如何强制编译器选择复制构造函数?

这是 mcve:

#include <iostream>

struct foo
{
    foo()
    {
        std::cout << "def constructor is invoked\n";
    }

    foo(const foo& other)
    {
        std::cout << "copy constructor is invoked\n";
    }

    template <typename T>
    foo(T&& value)
    {
        std::cout << "templated constructor is invoked\n";
    }
};

int main()
{
    foo first;
    foo second(first);
}

删除函数不是我想要的

添加另一个构造函数:

foo(foo& other) : foo( const_cast<const foo&>(other))  // for non-const lvalues
{
}

示例代码中的 first 对象是一个非常量左值,因此编译器更喜欢 foo(foo&) 而不是 foo(const &)。前者由模板提供(带T=foo&),因此被选中。

此解决方案涉及为 foo(foo&) 提供一个(非模板)构造函数,然后通过将构造函数转换为常量引用来将构造委托给复制构造函数

Update,我刚刚意识到 foo 右值也将被模板采用。这里有很多选项,但我想最简单的是也为 foo(foo&&) 添加一个委托,类似于上面的

foo(foo&& other) : foo( const_cast<const foo&>(other))  // for rvalues
{
}

问题在于 first 是可变的,因此对它的引用是 foo&,它比 const foo& 更容易绑定到通用引用 T&&

据推测,您打算 T 是任何非 foo class?

在这种情况下,一点点 enable_if 欺骗可以向编译器表达意图,而不必编写大量虚假重载。

#include <iostream>

struct foo
{
    foo()
    {
        std::cout << "def constructor is invoked\n";
    }

    foo(const foo& other)
    {
        std::cout << "copy constructor is invoked\n";
    }

    template <typename T, std::enable_if_t<not std::is_base_of<foo, std::decay_t<T>>::value>* = nullptr>
    foo(T&& value)
    {
        std::cout << "templated constructor is invoked\n";
    }

};

int main()
{
    foo first;
    foo second(first);
    foo(6);
}

预期输出:

def constructor is invoked
copy constructor is invoked
templated constructor is invoked