Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如果这一点的C++ CRTP代码编译,如果是这样,它应该怎么办?_C++_Templates_Inheritance_Overloading_Crtp - Fatal编程技术网

如果这一点的C++ CRTP代码编译,如果是这样,它应该怎么办?

如果这一点的C++ CRTP代码编译,如果是这样,它应该怎么办?,c++,templates,inheritance,overloading,crtp,C++,Templates,Inheritance,Overloading,Crtp,我正在考虑使用CRTP类来帮助重载,并想知道下面的代码会做什么: #include <iostream> #include <typeinfo> template <class TDerived> class FunctionImplementation { }; class AbstractFunction { }; class BaseFunction : public AbstractFunction, public FunctionImpleme

我正在考虑使用CRTP类来帮助重载,并想知道下面的代码会做什么:

#include <iostream>
#include <typeinfo>

template <class TDerived>
class FunctionImplementation
{
};

class AbstractFunction
{
};

class BaseFunction : public AbstractFunction, public FunctionImplementation<BaseFunction>
{
};

class DerivedFunction : public BaseFunction, public FunctionImplementation<DerivedFunction>
{
};

template <class TDerived>
void foo(const FunctionImplementation<TDerived>& function) {
    std::cout << "In foo for " << typeid(TDerived).name() << std::endl;
}

int main() {
    BaseFunction base;
    DerivedFunction derived;

    foo(base);
    foo(derived);
}
使用同一系统上的Clang 4.0,它在运行时编译并执行“自然”操作:

In foo for 12BaseFunction
In foo for 15DerivedFunction

使用Visual C++ 2010,它也编译,但运行方式不同:

In foo for class BaseFunction
In foo for class BaseFunction
最后,Linux上的GCC 4.7.2没有编译,但给出了一个更完整、更权威的错误消息:

overload.cpp: In function ‘int main()’:
overload.cpp:31:16: error: no matching function for call to ‘foo(DerivedFunction&)’
overload.cpp:31:16: note: candidate is:
overload.cpp:22:6: note: template<class TDerived> void foo(const FunctionImplementation<TDerived>&)
overload.cpp:22:6: note:   template argument deduction/substitution failed:
overload.cpp:31:16: note:   ‘DerivedFunction’ is an ambiguous base class of ‘const FunctionImplementation<TDerived>’

哪个是正确的?我不是语言标准方面的专家…

在这种情况下,我相信gcc是正确的。您要求编译器为您执行类型推断,问题是类型DerivedFunction的给定参数不是直接的函数实现,因此必须执行转换。此时,转换列表包括通过BaseFunction的FunctionImplementation和直接的FunctionImplementation。这两个选项之间没有顺序,因此编译器会带着歧义退出

本标准在§14.8.2.1【临时扣减呼叫】/4,5中对此进行了处理

第4段一般来说,演绎过程试图找到模板参数值,该值将在类型A如上所述转换后使演绎A与A相同。但是,有三种情况允许存在差异:

[……]

如果p是一个类且p具有形式简单模板id,则转换后的a可以是推导出的a的派生类。同样,如果p是指向形式简单模板id的类的指针,则转换后的a可以是指向推导出的a所指向的派生类的指针

第5款只有在类型扣减失败的情况下才考虑这些备选方案。如果它们产生多个可能的推断A,则类型推断失败


在第4段中,它允许类型推断选择参数类型的基础,在这种情况下,有两个这样的基础。第5段确定,如果应用前面的规则产生多个结果,则类型推断失败。

在这种情况下,我认为gcc是正确的。您要求编译器为您执行类型推断,问题是类型DerivedFunction的给定参数不是直接的函数实现,因此必须执行转换。此时,转换列表包括通过BaseFunction的FunctionImplementation和直接的FunctionImplementation。这两个选项之间没有顺序,因此编译器会带着歧义退出

本标准在§14.8.2.1【临时扣减呼叫】/4,5中对此进行了处理

第4段一般来说,演绎过程试图找到模板参数值,该值将在类型A如上所述转换后使演绎A与A相同。但是,有三种情况允许存在差异:

[……]

如果p是一个类且p具有形式简单模板id,则转换后的a可以是推导出的a的派生类。同样,如果p是指向形式简单模板id的类的指针,则转换后的a可以是指向推导出的a所指向的派生类的指针

第5款只有在类型扣减失败的情况下才考虑这些备选方案。如果它们产生多个可能的推断A,则类型推断失败


在第4段中,它允许类型推断选择参数类型的基础,在这种情况下,有两个这样的基础。第5段确定,如果应用上述规则产生多个结果,则类型推断失败。

好的,下面的答案是错误的。从阅读13.3.1p7开始,我一直相信

在候选函数是函数模板的每种情况下,候选函数模板专用化都是使用模板参数推断14.8.3和14.8.2生成的。然后,按照通常的方式将这些候选函数作为候选函数处理

模板参数推导使用了适当的重载解析机制,从语法上可能的专门化中选择函数重载解析,等等

事实证明并非如此:模板参数推导有自己的、非常有限的一套规则,坚持精确匹配速度cv限定符和取消引用等,仅允许派生类进行模板化基参数转换,这在这里被视为一种特殊情况,并且这种特殊情况明确禁止使用函数重载解析来处理任何歧义

因此,正确答案见上文。我把这个答案留在这里是因为它得到了越来越多的选票,让我相信我不是唯一一个这样错误的人:

FooDevered的重载解析正在派生类中查找FunctionImplementation声明。派生的类没有该模板的成员范围声明,因此将其基类的递归查找结果合并,从而在其层次结构中产生两种专门化:

Derived
:   Base
    :   AbstractFunction
    ,   FunctionImplementation<Base>
,   FunctionImplementation<Derived>
考虑到 在执行名称查找时,在基类派生层次结构中找到声明的深度意味着在不影响使用多重继承的所有派生类中以前的结果的情况下,任何名称或基类都不能添加到类中。相反,C++拒绝选择,并使用声明来显式声明TePaFASE使用的是什么基类,在这种情况下,名称是您所指的。
标准见10.2,p3-5是肉。

好的,下面的答案是错误的。从阅读13.3.1p7开始,我一直相信

在候选函数是函数模板的每种情况下,候选函数模板专用化都是使用模板参数推断14.8.3和14.8.2生成的。然后,按照通常的方式将这些候选函数作为候选函数处理

模板参数推导使用了适当的重载解析机制,从语法上可能的专门化中选择函数重载解析,等等

事实证明并非如此:模板参数推导有自己的、非常有限的一套规则,坚持精确匹配速度cv限定符和取消引用等,仅允许派生类进行模板化基参数转换,这在这里被视为一种特殊情况,并且这种特殊情况明确禁止使用函数重载解析来处理任何歧义

因此,正确答案见上文。我把这个答案留在这里是因为它得到了越来越多的选票,让我相信我不是唯一一个这样错误的人:

FooDevered的重载解析正在派生类中查找FunctionImplementation声明。派生的类没有该模板的成员范围声明,因此将其基类的递归查找结果合并,从而在其层次结构中产生两种专门化:

Derived
:   Base
    :   AbstractFunction
    ,   FunctionImplementation<Base>
,   FunctionImplementation<Derived>
考虑到在执行名称查找时在基类派生层次结构中找到声明的深度,意味着在不影响使用多重继承的所有派生类中以前的结果的情况下,任何名称或基类都无法添加到类中。相反,C++拒绝选择,并使用声明来显式声明TePaFASE使用的是什么基类,在这种情况下,名称是您所指的。
这方面的标准见10.2,p3-5是关键。

使用类作为类的基类似乎有点可疑。你的意思是从BaseFunction派生DerivedFunction,因为这意味着DerivedFunction有两个FunctionImplementation基类?这不是实际的代码…我在考虑用一个抽象基类AbstractFunction建立一个类层次结构,然后每个叶类CRTP都继承自FunctionImplementation,因此我可以拥有一个函数,实际上是一个构造函数,在我的例子中,它将接受“AbstractFunction的所有叶子类”。然后我想知道如果有人可能是我自己会发生什么!意外地误用了代码,从其中一个叶类中进行子类化,并再次从FunctionImplementation继承新的叶类,所以我决定进行实验,看看会发生什么。使用一个类作为该类的基类似乎是可疑的。你的意思是从BaseFunction派生DerivedFunction,因为这意味着DerivedFunction有两个FunctionImplementation基类吗?这不是实际的代码…我在考虑用一个抽象基类建立一个类层次结构AbstractFunction,然后让每个叶类CRTP从FunctionImplementation继承,这样我就可以拥有一个实际上是构造函数的函数,在我的例子中,它将接受“AbstractFunction的所有叶子类”。然后我想知道如果有人可能是我自己会发生什么!无意中误用了代码,从其中一个叶类中进行子类化,并再次从FunctionImplementation继承了新的叶类,因此我决定进行实验,看看会发生什么。完美-正是我想要的。我希望Clang和Visual Studio能够正确地实现这一点,这样我就可以依靠编译器捕捉错误,以确保类层次结构设置正确。@IanMackenzie:不清楚您试图实现什么。您希望只使用最派生的实例吗?这很容易,也就是说,通过添加一个接受T并调用foo的调度程序模板,可以使编译器之间的行为保持一致,或者您希望根本不进行此编译?你想解决的真正问题是什么?可能有比这更简单的方法…@dribeas:我有一个函数类,它包含一个指向抽象函数pimpl习惯用法的指针,并且可以从这样一个指针函数Function=new-ConcreteFunction来构造。。。。我试图找到一种方法来允许函数=具体函数。。。同时保留重载。例如,有运算符+函数这样的函数,若函数有来自ar的构造函数,那个么函数将真的把事情搞砸
T.T.T.我可能会用Enable If If和/或C++ 11最终类做一些事情,但最终决定整个想法都是坏的。“IANMAKZEZI:不确定我完全理解你想要什么,但是你可以考虑使用类型擦除的方式和STD::函数一样。让函数按常量引用获取一个具体函数,并在内部将其包装到存储在函数对象内的动态分配的新ed对象中。@dribeas:这正是我计划要做的,但我正在寻找一种干净的方法来为许多不同类型的具体函数SumFunction执行此操作,ProductFunction等,而不必为每个函数都有不同的函数构造函数重载。完美-正是我所寻找的。我希望Clang和Visual Studio能够正确地实现这一点,这样我就可以依靠编译器捕捉错误,以确保类层次结构设置正确。@IanMackenzie:不清楚您试图实现什么。您希望只使用最派生的实例吗?这很容易,也就是说,通过添加一个接受T并调用foo的调度程序模板,可以使编译器之间的行为保持一致,或者您希望根本不进行此编译?你想解决的真正问题是什么?可能有比这更简单的方法…@dribeas:我有一个函数类,它包含一个指向抽象函数pimpl习惯用法的指针,并且可以从这样一个指针函数Function=new-ConcreteFunction来构造。。。。我试图找到一种方法来允许函数=具体函数。。。同时保留重载。例如,有运算符+函数这样的函数,如果函数有一个来自任意T的构造函数,函数会把事情搞得一团糟。我可能可以用enable_if和/或C++11 final类来做一些事情,但最终决定整个想法是错误的。@IanMackenzie:我不确定我完全理解你想要什么,但是您可以考虑使用类型擦除的方式与STD::函数一样。让函数按常量引用获取一个具体函数,并在内部将其包装到存储在函数对象内的动态分配的新ed对象中。@dribeas:这正是我计划要做的,但我正在寻找一种干净的方法来为许多不同类型的具体函数SumFunction执行此操作,ProductFunction等,而不必为每个函数都设置不同的函数构造函数重载。我唯一的疑问是,基是否会像我和您所相信的那样变平,或者按照10.2节进行查找。如果它遵循10.2节,根据第5段,那么VS和clang是正确的,因为查找集仅用直接基扩展。另一方面,我的感觉是,当涉及转换时,查找根本不遵循10.2节。考虑结构A {};结构B:A{};结构C:A,B{};无效fA;使用C实例调用f是不明确的,因为有两个派生到基类的转换可用…@dribeas:我想你的意思是VS和Clang都是正确的,因为它们都接受代码-我不知道使用间接基类的VS编译代码的实际行为如何,即使直接基类可用可能是正确的。在我看来,这是唯一一个明显错误的=@DavidRodríguez dribeas我不明白你是如何从10.2中得到它的-见10.2p5,它是所有碱基的明确结合declarations@jthill:10.2p5否则,即C不包含f声明或生成的声明集为空,Sf,C最初为空。如果C有基类,计算每个直接基类子对象Bi中f的查找集,并将每个这样的查找集Sf,Bi依次合并为Sf,C。-查找不会一次合并所有基类的结果,它会一次向上移动一级,直到找到第一个元素,然后停止函数隐藏的原因,可能是VS/clang这样做的原因that@IanMackenzie:我错过了VS的输出。。。这是毫无意义的。我的解释是,gcc是正确的,因为常规查找不是标准规定的转换,您可以在所有编译器中使用第一条注释中的代码来测试它。在clang中的实现可能遵循通常的查找规则,并在发现第一个匹配错误后停止。在我看来,VS只是胡说八道。我唯一的疑问是,基础是否会像我和你似乎相信的那样变平,或者按照10.2节进行查找。如果它遵循10.2节,根据第5段,那么VS和clang是正确的,因为查找集仅用直接基扩展。另一方面,我的感觉是,当涉及转换时,查找根本不遵循10.2节。考虑结构A {};结构B:A{};结构C:A,B{};无效fA;使用C实例调用f是不明确的,因为有两个派生到基的转换可用…@dribeas:我想你的意思是VS和Clang都是正确的,因为它们都是ac
除了代码,我不认为使用间接基类的VS编译代码的实际行为可能是正确的,即使直接基类可用。在我看来,这是唯一一个明显错误的=@DavidRodríguez dribeas我不明白你是如何从10.2中得到它的-见10.2p5,它是所有碱基的明确结合declarations@jthill:10.2p5否则,即C不包含f声明或生成的声明集为空,Sf,C最初为空。如果C有基类,计算每个直接基类子对象Bi中f的查找集,并将每个这样的查找集Sf,Bi依次合并为Sf,C。-查找不会一次合并所有基类的结果,它会一次向上移动一级,直到找到第一个元素,然后停止函数隐藏的原因,可能是VS/clang这样做的原因that@IanMackenzie:我错过了VS的输出。。。这是毫无意义的。我的解释是,gcc是正确的,因为常规查找不是标准规定的转换,您可以在所有编译器中使用第一条注释中的代码来测试它。clang中的实现可能遵循通常的查找规则,并在发现第一个匹配错误后停止。在我看来,VS只是胡说八道。