5

次のコードは機能していると思いますが、g++ と clang++ の両方がまったく同じエラーを返します (ただし、Visual C++ 2012 はそうではありません)。

#include <iostream>
#include <tuple>

template <int N, typename T>
struct A { };

template <typename Tuple>
double result(const Tuple& t, const A<0, typename std::tuple_element<0, Tuple>::type>& a)
{
  return 0;
}

template <typename Tuple>
double result(const Tuple& t, const A<std::tuple_size<Tuple>::value-1,
                                      typename std::tuple_element<std::tuple_size<Tuple>::value-1,Tuple>::type>& a)
{
  return 1;
}

template <typename Tuple, int N>
double result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 0.5;
}

int main()
{
  auto a = std::make_tuple(0, 1, 2., 3., 4);
  std::cout << result(a, A<0,int>()) << std::endl;
  std::cout << result(a, A<2,double>()) << std::endl;
  std::cout << result(a, A<4,int>()) << std::endl; // Fails if uncommented
  return 0;
}

このエラーは、最後の行と、2 番目と 3 番目のresult関数が同等と見なされていることが原因です。2番目のものは3番目のものよりも適していると思いますが(最初のもののように).

よくわかりませんが。私が間違っているのか、それともコンパイラが間違っているのか、誰か教えてもらえますか?

4

3 に答える 3

6

TLDR; The reason that your program fails to compile is that the second and third overload are equally good matches during overload resolution. In particular, neither is more specialized than the other. Because overload resolution cannot select a best match, the program is ill-formed. The cure is to SFINAE your way out of it.

The problem

14.5.6.2 Partial ordering of function templates [temp.func.order]

2 Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

3 To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

For all three overloads, the first synthesized argument is equal, and since all arguments are considered one by one, we can focus on the second one.

Your first overload is transformed to the following synthesized 2nd argument

const A<0, typename std::tuple_element<0, Arg1>::type>&

Your second overload is transformed to the following synthesized 2nd argument

const A<
        std::tuple_size<Arg1>::value-1, typename        
        std::tuple_element<std::tuple_size<Arg1>::value-1, Arg1>::type
>&

Your third overload is transformed to the following synthesized 2nd argument

const A<Arg2, typename std::tuple_element<Arg2, Arg1>::type>&    

14.8.2.4 Deducing template arguments during partial ordering [temp.deduct.partial]

2 Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [ Note: The creation of the transformed type is described in 14.5.6.2. — end note ] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.

It is clear that the first and second overload have no 2nd template parameter to deduce and so they are at least as specialized as the third overload. The question is whether the third can have it's N parameter deduced from the first and second overloads' synthesized 2nd argument.

For the first overload, this is true for N=0, and so the first overload is more specialized than the third. This is why your first function call selects the first overload.

For the third overload, argument deduction does not take place it is a non-deduced context:

14.8.2.5 Deducing template arguments from a type [temp.deduct.type]

5 The non-deduced contexts are:

— ...

A non-type template argument or an array bound in which a subexpression references a template parameter.

— ...

This means that the third overload is also at least as specialized as the second one. Hence, overload resolution is not able to select one, and the program is ill-formed.

The cure

Simply make two overloads with a non-overlapping condition inside an enable_if (using SFINAE). This bypasses overload resolution in this case.

template <typename Tuple, int N>
typename std::enable_if<N == std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 1;
}

template <typename Tuple, int N>
typename std::enable_if<N != std::tuple_size<Tuple>::value-1, double>::type
result(const Tuple& t, const A<N, typename std::tuple_element<N, Tuple>::type>& a)
{
  return 0.5;
}

Live Example.

于 2013-10-16T14:45:29.163 に答える