문제

I was thinking about using CRTP classes to help with overloading and wondered what the following bit of code would do:

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

With GCC 4.2 on OS X it doesn't compile:

overload.cpp: In function ‘int main()’:
overload.cpp:31: error: no matching function for call to ‘foo(DerivedFunction&)’

With Clang 4.0 on the same system, it compiles and does the 'natural' thing when it runs:

In foo for 12BaseFunction
In foo for 15DerivedFunction

With Visual C++ 2010, it also compiles but runs differently:

In foo for class BaseFunction
In foo for class BaseFunction

Finally, GCC 4.7.2 on Linux does not compile but gives a more complete and fairly authoritative-sounding error message:

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>’

Which is correct? I am no expert at navigating the language standard...

도움이 되었습니까?

해결책

In this case I believe gcc is right. You are asking the compiler to perform type deduction for you, and the problem is that the given argument of type DerivedFunction is not a FunctionImplementation<TDerived> directly, so a conversion has to be performed. At this point the list of conversions includes FunctionImplementation<BaseFunction> (through BaseFunction) and FunctionImplementation<DerivedFunction> (directly). There is no ordering among those two choices, so the compiler bails out with an ambiguity.

The standard treats this in §14.8.2.1 [temp.deduct.call]/4,5

(paragraph 4) In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

[...]

If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.

(paragraph 5) These alternatives are considered only if type deduction would otherwise fail. If they yield more than one possible deduced A, the type deduction fails.

In the 4th paragraph it allows type deduction to pick a base of the argument type, in this case there are 2 such bases. The 5th paragraph determines that if the application of the previous rule yields more than one result, then type deduction fails.

다른 팁

Okay, the answer below is wrong. I had always believed from reading 13.3.1p7

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.

that template argument deduction used the appropriate overload resolution machinery to choose among the syntactically-possible specializations (function overload resolution for functions, etc.).

It turns out that's not true: template argument deduction has its own, very restricted set of rules that insist on exact matches (pace cv-qualifiers and dereferencing and the like), only permitting the derived-class-to-templated-base argument conversion seen here as a special case -- and that special case explicitly forbids using function overload resolution to handle any ambiguities.

So for the right answer, see above. I'm leaving this answer here because it's getting upvotes, leading me to believe I'm not the only one who has it wrong this way:

Overload resolution for foo(derived) is looking up FunctionImplementation<T> declarations in class Derived. Class Derived has no member-scope declarations of that template, so the recursive lookup results from its base classes are combined, yielding both of the specializations in its hierarchy:

Derived
:   Base
    :   AbstractFunction
    ,   FunctionImplementation<Base>
,   FunctionImplementation<Derived>

Considering the depth at which a declaration was found within the base-class derivation hierarchy when doing name lookup would mean no name or base could ever be added to a class without silently affecting previous results in all derived classes that use multiple inheritance. Instead, C++ refuses to pick for you and supplies using-declarations to explicitly declare which base class's use of the (tepmlate, in this case) name is the one you mean to refer to.

The standardese for this is in 10.2, p3-5 are the meat.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top