如何防止可变参数构造函数优先于复制构造函数?

acm*_*acm 33 c++ copy-constructor variadic-templates c++11

我有一个模板'Foo',它拥有一个T,我希望它有一个可变参数构造函数,将它的参数转发给T的构造函数:

template<typename T>
struct Foo {

    Foo()
        : t() {}

    Foo(const Foo& other)
        : t(other.t) {}

    template<typename ...Args>
    Foo(Args&&... args)
        : t(std::forward<Args>(args)...) {}

    T t;
};
Run Code Online (Sandbox Code Playgroud)

但是,这会导致Foo无法复制:

int main(int argc, char* argv[]) {
    Foo<std::shared_ptr<int>> x(new int(42));
    decltype(x) copy_of_x(x);  // FAILS TO COMPILE
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

因为,根据这个答案,参数的非常量导致可变参数构造函数更好地匹配.出于显而易见的原因,我不想强​​制我的调用者使用const_cast.

我找到的一个可能的解决方案是为Foo编写一个"复制构造函数",它采用非const Foo并使用构造函数转发:

Foo(Foo& other)
    : Foo(const_cast<const Foo&>(other)) {}
Run Code Online (Sandbox Code Playgroud)

当定义了这个构造函数时,事情再次起作用:现在首选非const Foo参数copy ctor.然而,这对我来说似乎非常粗略,因为这种"治愈"似乎比疾病更糟糕.

是否有另一种方法来实现这种效果,表明自然拷贝构造函数应该优先于可变参数构造函数?如果没有,定义这个非const参数复制构造函数会有什么不利后果吗?

Luc*_*lle 14

你可以使用一些丑陋的SFINAE std::enable_if,但我不确定它比你的初始解决方案更好(事实上,我很确定它更糟糕!):

#include <memory>
#include <type_traits>

// helper that was not included in C++11
template<bool B, typename T = void> using disable_if = std::enable_if<!B, T>;

template<typename T>
struct Foo {

    Foo() = default;
    Foo(const Foo &) = default;

    template<typename Arg, typename ...Args, typename = typename
        disable_if<
            sizeof...(Args) == 0 &&
            std::is_same<typename
                std::remove_reference<Arg>::type,
                Foo
            >::value
        >::type
    >
    Foo(Arg&& arg, Args&&... args)
        : t(std::forward<Arg>(arg), std::forward<Args>(args)...) {}

    T t;
};

int main(int argc, char* argv[]) {
    Foo<std::shared_ptr<int>> x(new int(42));
    decltype(x) copy_of_x(x);
    decltype(x) copy_of_temp(Foo<std::shared_ptr<int>>(new int));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)