質問

Explicit template arguments on function templates are again and again a source of surprise for me even in the context of trivial code. Should such code be considered as anti pattern? Some developers have postulated this in other StackOverflow threads.

In the following code the compiler reports an error at code position (1) stating that a suitable function overload is not found. I would have been less surprised if the compiler detected ambiguity but this is not the case. Any hint? I am using gcc 4.6.2

EDIT: I am not looking for a solution. An example solution is documented in the cppreference. I am looking for an explanation.

#include <iostream>
#include <tuple>
#include <type_traits>

using namespace std;

template<typename T, size_t N = 1>
//typename enable_if<integral_constant<bool, N==1>::value, ostream>::type &
ostream & operator << (ostream& stream, tuple<T> const & t)
{
        return stream << get<0>(t) << endl;
}

template<typename ...T, size_t N = sizeof...(T)>
//typename enable_if<integral_constant<bool, N!=1>::value, ostream>::type &
ostream & operator << (ostream& stream, tuple<T...> const & t)
{
        operator << <T...,N-1> (stream, t); // (1) compile error
        return stream << get<N-1>(t) << endl;
}

int main ()
{
        auto t = make_tuple ("hallo", 1L, 1.1);
        cout << t << endl;
}
役に立ちましたか?

解決

You could delete the first overload and the effect would be the same. The first overload is never a candidate because it takes a tuple with only one T.

Looking at the second overload, the problem is that you have the wrong order of the template parameters, namely the size_t and the parameter pack T.... The compiler needs to have the parameter pack at the end. The rules for this are basically:

  • You can only have one parameter pack which is explicitly specified.
  • This must be the last explicitly specified parameter.
  • All other parameters (if any) must be deduced from the arguments or they must have a default.

Here's something that allows the recursive call itself, due to swapping the parameters, but now the compiler itself will end up with an endless recursion:

#include <iostream>
#include <tuple>

using namespace std;

template<size_t N, typename ...T>
ostream & print (ostream& stream, tuple<T...> const & t)
{
    print< N-1, T... > (stream, t);
    return stream << get<N-1>(t) << endl;
}

template<typename ...T>
ostream & operator << (ostream& stream, tuple<T...> const & t)
{
    return print< sizeof...(T), T... >( stream, t );
}

int main ()
{
    auto t = make_tuple ("hallo", 1L, 1.1);
    cout << t << endl;
}

You can try it out (nothing really bad will happen) and after some time, you'll receive an error message from the compiler.

To illustrate the problem further, look at this reduced example:

#include <tuple>

// everything after the first parameter pack *must* be deduced or come from a default
template<typename ...T, int S=0>
void f(const std::tuple<T...>& arg) {}

int main() {
    // OK
    f(std::tuple<int>(1));
    f<int>(std::tuple<int>(1));

    // Fail
    f<int,0>(std::tuple<int>(1));
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top