Ambiguous partial specializations with std :: enable_if

I have a problem that occurs under conditions like below:

#include <iostream> #include <type_traits> #define TRACE void operator()() const { std::cerr << "@" << __LINE__ << std::endl; } template <class T> struct check : std::true_type {}; template <class F, class T, class Check=void> struct convert { TRACE;// first case }; template <class F, class T> struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> { TRACE; // second case }; template <class T> struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> { TRACE; // third case }; 

Then

 convert<int*, int> c; c(); 

will report an ambiguous instance of the class instance in g ++ - 4.5, g ++ - 4.6, g ++ - 4.7 and clang ++ - 3.1 (all with the -std = C ++ 0x option)

But if I replace the check in the third case with

 typename std::enable_if<(check<int>::value && check<T>::value), void>:type 

Then clang ++ - 3.1 works fine.

Is this a compiler error or a standard one?

+6
source share
2 answers

You have

 template <class F, class T> struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> { TRACE; // second case }; 

and

 template <class T> struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> { TRACE; // third case }; 

When you use convert<int*, int> c; , the compiler cannot choose which of the structures it should use, because they both fit.

Note that you are using check<F>::value in the first template. This means that even if you pass, for example, int * , you will have check<int>::value , not check<int *>::value

0
source

Similar problems have arisen in this matter.

Since both the second and third partial specializations correspond to convert<int*, int> , the compiler will build two test function templates with two partially specialized class templates provided as arguments:

 template <class F, class T> void fun2(convert<F*, T, typename std::enable_if< (check<F>::value && check<T>::value), void>::type> ); template <class T> void fun3(convert<int*, T, typename std::enable_if< (check<T>::value), void>::type> ); 

Then the compiler checks whether one function template is more specialized than another, cross-substituting the set of converted parameters of one function into another and checking whether all arguments of the template can be output. If this works in both directions, then none of the functions is more specialized than the other, and ambiguity arises.

The problem here is that std::enable_if< (check<F>::value && check<T>::value), void>::type> is an uninformed context that will not be evaluated during this output game arguments. The compiler only checks if the common expressions have the same structural form (where anything before the separator :: output), and not if they have the same value ( true_type in this case).

Only by adding the extra check<int>::value to the third partial specialization does the third specialization become more specialized than the second. Another β€œfix” would be to manually put true_type in the Check parameter, however the compiler does not do this for you during the output of the argument.

UPDATE In response to Johannes Schaub - litb: you're right, the code with std::check<int> , placed in std::enable_if , does not compile on Ideone and MSVC ++ 2010. What can I do from this? According to 14.8.2.4, clause 11 [temp.deduct.partial]

In most cases, all template parameters must have values ​​in order to deduct for success, but for partial sequencing, the template parameter can remain without a value if it is not used in the types used for partial ordering. [Note: the template parameter is used in a non-deduced context. - final note] [Example:

 template <class T> T f(int); // #1 template <class T, class U> T f(U); // #2 void g() { f<int>(1); // calls #1 } 

For OP code, I interpret this that the std::enable_if expression will be an unused parameter. My assumption would be that Clang 3.1 does not execute some expressions corresponding to Ideone and MSVC ++. I do not understand if, in the context of the above quote, this is required or not by the standard: should only unused template parameters or also unused template expressions be ignored? There are other parts in the standard where phrases like "not requiring implementation for using heroic eff orts" appear. Perhaps Clang is more heroic than MSVC ++ or Ideone in this regard.

0
source

Source: https://habr.com/ru/post/920561/


All Articles