Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
248 views
in Technique[技术] by (71.8m points)

c++ - How can I prevent a variadic constructor from being preferred to the copy constructor?

I have a template 'Foo', which owns a T, and I'd like it to have a variadic constructor that forwards its arguments to T's constructor:

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;
};

However, this causes Foo to not be copyable:

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;
}

because, according to this answer, the non-constness of the argument causes the variadic constructor to be a better match. I don't want to force my callers to use const_cast, for obvious reasons.

One possible solution I found was to write a "copy constructor" for Foo which takes a non-const Foo and uses constructor forwarding:

Foo(Foo& other)
    : Foo(const_cast<const Foo&>(other)) {}

When this constructor is defined, things work again: now the non-const Foo argument copy ctor is preferred. However, this seems very sketchy to me, in that this "cure" seems worse than the disease.

Is there another way to achieve this effect, to indicate that the natural copy constructor should be preferred to the variadic constructor? If not, are there any adverse consequences of defining this non-const argument copy constructor?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can use some ugly SFINAE with std::enable_if, but I'm not sure it is better than your initial solution (in fact, I'm pretty sure it's worse!):

#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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...