调用通用构造函数模板而不是 copy/move 构造函数

Generic constructor template called instead of copy/move constructor

我设计了一个更简单的包装器 class,它向对象添加标签,目的是隐式 convertible/able 替换包装的对象。

#include <string>
#include <type_traits>
#include <utility>

template < typename T, typename Key = std::string >
class myTag{
    T val;
 public:
    Key key;

    template < typename... Args,
               typename = typename std::enable_if< std::is_constructible< T, Args... >::value >::type >
    myTag(Args&&... args) :
        val(std::forward< Args >(args)...) {
            std::cout << "forward ctor" << std::endl;
    }

    myTag(const myTag& other) :
        key(other.key), val(other.val) {
            std::cout << "copy ctor" << std::endl;
    }

    myTag(myTag&& other):
        key(other.key), val(other.val) {
            std::cout << "move ctor" << std::endl;
    }

    operator T&() { return val; }

    operator const T&() const { return val; }
};


int main(int argc, char const *argv[]) {
    myTag< float > foo(5.6);   // forward ctor
    myTag< float > bar(foo);   // forward ctor

    return 0;
}

但是,我无法正确声明和定义 copy/move 构造函数。我声明了一个通用构造函数重载模板,它将其参数转发给基础类型,只要这样的构造是可能的。但是,由于隐式转换运算符,它捕获了 myTag 的每个实例化,有效地隐藏了 copy/move 构造函数。

非默认 copy/move 语义的要点是 copy/move key 值与使用构造函数模板默认初始化它。

如何使编译器 prefer/give 优先于显式 copy/move 构造函数与泛型重载?是否有任何额外的 SFINAE 检查替代 is_constructible<> 来避免隐式转换?

编辑:我应该补充一点,我正在寻找 C++14 解决方案。

它没有隐藏复制和移动构造函数。它只是在某些情况下在重载解决方案中击败了他们。

  • 如果传递 myTag<float>&,则使用转发构造函数。
  • 如果您传递 const myTag<float>&,则使用复制构造函数。
  • 如果传递 myTag<float>&&,则使用移动构造函数。
  • 如果传递 const myTag<float>&&,则使用转发构造函数。

复制和移动构造函数不是模板,因此它们将胜过具有相同签名的模板。但是在推导的签名不同于复制和移动构造函数的情况下,转发构造函数可以更好地匹配。

处理此问题的惯用方法是根据参数的衰减类型从考虑中删除转发构造函数:

template <typename... Args>
constexpr myTag(Args&&... args)
    requires
        ((sizeof...(Args) != 1 && std::is_constructible_v<T, Args...>) ||
         (sizeof...(Args) == 1 && std::is_convertible_v<Args..., T> && !std::is_same_v<myTag, std::decay_t<Args>...>))