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

C++ 如何防止可变构造函数被优先于复制构造函数?,c++,c++11,copy-constructor,variadic-templates,C++,C++11,Copy Constructor,Variadic Templates,我有一个模板“Foo”,它拥有一个T,我希望它有一个变量构造函数,将其参数转发给T的构造函数: template<typename T> struct Foo { Foo() : t() {} Foo(const Foo& other) : t(other.t) {} template<typename ...Args> Foo(Args&&... args) :

我有一个模板“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;
};
模板
结构Foo{
Foo()
:t(){}
Foo(const Foo&其他)
:t(其他.t){}
模板
Foo(Args&&…Args)
:t(std::forward,参数的非常量导致变量构造函数更匹配。出于明显的原因,我不想强制调用方使用常量转换

我发现的一个可能的解决方案是为Foo编写一个“复制构造函数”,它接受一个非常量Foo并使用构造函数转发:

Foo(Foo& other)
    : Foo(const_cast<const Foo&>(other)) {}
Foo(Foo和其他)
:Foo(const_cast(其他)){}
当这个构造函数被定义后,事情又开始了:现在非常量Foo参数copy-ctor是首选。然而,这对我来说似乎非常粗略,因为这种“治愈”似乎比疾病更糟糕


有没有其他方法可以达到这种效果,表明自然复制构造函数应该优先于可变构造函数?如果没有,定义这个非常量参数复制构造函数是否有任何不利后果?

您可以使用一些丑陋的SFINAE和
std::enable\u If
,但我不确定它是否比您的最初的解决方案(事实上,我很确定情况会更糟!)

#包括
#包括
//C++11中未包含的帮助程序
模板使用disable_if=std::enable_if;
模板
结构Foo{
Foo()=默认值;
Foo(const Foo&)=默认值;
模板::类型
>
Foo(Arg&&Arg,Args&&Args)
:t(std::forward(arg),std::forward(args)…{}
T;
};
int main(int argc,char*argv[]){
富x(新国际(42);;
decltype(x)x(x)的副本;
decltype(x)临时文件(Foo(new int))的副本;
返回0;
}

最好的方法是不要做你正在做的事情

这就是说,一个简单的修复方法是让可变构造函数向上转发到基类构造函数,并带有一些特殊的第一个参数

例如,使用MinGW g++4.7.1编译以下内容:

#include <iostream>         // std::wcout, std::endl
#include <memory>           // std::shared_ptr
#include <stdlib.h>         // EXIT_SUCCESS
#include <tuple>
#include <utility>          // std::forward

void say( char const* const s ) { std::wcout << s << std::endl; }

template<typename T>
struct Foo;

namespace detail {
    template<typename T>
    struct Foo_Base
    {
        enum Variadic { variadic };

        Foo_Base()
            : t()
        { say( "default-init" ); }

        Foo_Base( Foo_Base const& other )
            : t( other.t )
        { say( "copy-init" ); }

        template<typename ...Args>
        Foo_Base( Variadic, Args&&... args )
            : t( std::forward<Args>(args)... )
        { say( "variadic-init" ); }

        T t;
    };

    template<typename T>
    struct Foo_ConstructorDispatch
        : public Foo_Base<T>
    {
        Foo_ConstructorDispatch()
            : Foo_Base<T>()
        {}

        template<typename ...Args>
        Foo_ConstructorDispatch( std::tuple<Foo<T>&>*, Args&&... args )
            : Foo_Base<T>( args... )
        {}

        template<typename ...Args>
        Foo_ConstructorDispatch( std::tuple<Foo<T> const&>*, Args&&... args )
            : Foo_Base<T>( args... )
        {}

        template<typename ...Args>
        Foo_ConstructorDispatch( void*, Args&&... args)
            : Foo_Base<T>( Foo_Base<T>::variadic, std::forward<Args>(args)... )
        {}
    };
}  // namespace detail

template<typename T>
struct Foo
    : public detail::Foo_ConstructorDispatch<T>
{
    template<typename ...Args>
    Foo( Args&&... args)
        : detail::Foo_ConstructorDispatch<T>(
            (std::tuple<Args...>*)0,
            std::forward<Args>(args)...
            )
    {}
};

int main()
{
    Foo<std::shared_ptr<int>>   x( new int( 42 ) );
    decltype(x)                 copy_of_x( x );
}
#包括//std::wcout、std::endl
#include//std::shared\u ptr
#包括//退出\成功
#包括
#include//std::forward
void say(字符常量*常量s){std::wcout
如果不是,那么定义这个非常量参数复制构造函数是否有任何不利后果

我将忽略“If not”,因为还有其他方法。但是您的方法会产生一个不利的后果。下面仍然使用模板构造函数

Foo<X> g();
Foo<X> f(g());
foog();
foof(g());

因为
g()
是一个右值,所以模板更匹配,因为它将参数推断为右值引用。

当参数类型与此类型相同或派生自此类型时,禁用构造函数:

template<typename ThisType, typename ... Args>
struct is_this_or_derived : public std::false_type {};

template<typename ThisType, typename T>
struct is_this_or_derived<ThisType, T>
    : public std::is_base_of<std::decay_t<ThisType>, std::decay_t<T> >::type {};

template<typename ThisType, typename ... Args>
using disable_for_this_and_derived 
      = std::enable_if_t<!is_this_or_derived<ThisType, Args ...>::value>;
模板
结构是\u this\u或\u派生的:public std::false\u type{};
模板
结构是\u this \u还是\u派生的
:public std::is_base_of::type{};
模板
将禁用\u用于此\u和导出的\u
=std::如果_t::value>,则启用_;
把它当作

template<typename ...Args
        , typename = disable_for_this_and_derived<Foo, Args ...> >
                                                //^^^^
                                                //this needs to be adjusted for each class
Foo(Args&&... args) : t(std::forward<Args>(args)...) {}
模板
//^^^^
//这需要针对每个类进行调整
Foo(Args&&…Args):t(std::forward(Args)…..{}

我最初猜测的可能重复是,仅有的两种解决方案要么是非常量的额外重载,要么是排除这种情况的某些enable_if逻辑。就个人而言,我会添加一个非常量复制构造函数。@BoPersson不是真正的重复。我读过这个问题和答案(甚至链接到它),但我的问题更多的是关于将非const arg copy ctor声明为解决方法是否会产生负面影响。相关用途:我需要考虑一下这个问题(typename=typename!),但我怀疑您的结论可能是正确的。@acm:C++11允许函数使用默认模板参数,C++03不允许。如果@acm使用
sizeof…(Args),则可以方便地应用
enable\u==0
也可以检查。即,如果在
参数中有一个或多个元素,则启用该元素,如果第一个元素不是
Foo
@acm:about
typename=typename…
,则启用该元素为“简单”带有默认值的未命名模板参数。另请参见上的此页。@Yakk:的确,如果有多个参数,并且第一个参数是
Foo
,我给出的代码将失败。让met更正。当然。我实际上在减少测试用例以制作示例时删除了move-ctor,因为我不想让事情变得更复杂例如,至少对于clang,当Foo(Foo&&)时,它似乎没有选择模板是可用的。谢谢,这是一个有趣的方法。不幸的是,我认为在我试图构建的情况下,额外的复杂性可能没有帮助。我可能会删除使用模板构造函数的尝试。虽然在某些方面很好,但显然这比它值得的麻烦多。@acm:“不要做你正在做的事"建议意味着不要直接将所有可变构造函数参数传递给包含的东西。您仍然可以通过这种方式轻松处理实际问题,但第一个参数仅表示,请将其传递给包含的东西。本质上,直接向用户提供上面显示的
Foo_Base
类,并带有显式constructor选项,而不是上面的
Foo
类和隐式(自动)构造函数选项(这是您要求的)。Explicit=good,implicit=bad。很抱歉要详细说明,但可能不清楚!:-)这种有趣的方法有一个巨大的缺点:默认ctor、复制ctor和移动ctor都不再是微不足道的…@Deduplicator:我看不出转发构造函数不是微不足道的缺点。@cheers和hth。
template<typename ThisType, typename ... Args>
struct is_this_or_derived : public std::false_type {};

template<typename ThisType, typename T>
struct is_this_or_derived<ThisType, T>
    : public std::is_base_of<std::decay_t<ThisType>, std::decay_t<T> >::type {};

template<typename ThisType, typename ... Args>
using disable_for_this_and_derived 
      = std::enable_if_t<!is_this_or_derived<ThisType, Args ...>::value>;
template<typename ...Args
        , typename = disable_for_this_and_derived<Foo, Args ...> >
                                                //^^^^
                                                //this needs to be adjusted for each class
Foo(Args&&... args) : t(std::forward<Args>(args)...) {}