简体   繁体   中英

Why does this template argument deduction fail when a default value is given?

The following code works:

template <typename T>
struct Foo
{
    template <typename OStream>
    static void default_print_function(OStream &, const T &);

    template <typename OStream, typename PrintFunction>
    void print(
        OStream &,
        const PrintFunction & = &Foo<T>::default_print_function<OStream>) const;

    template <typename OStream>
    void print(OStream &) const;
};


template <typename T>
template <typename OStream>
void Foo<T>::default_print_function(OStream & o, const T & value)
{
    o << value;
}

template <typename T>
template <typename OStream, typename PrintFunction>
void Foo<T>::print(OStream & o, const PrintFunction & f)
const
{
    T test_value = 123;

    o << "TEST: ";
    f(o, test_value);
}

template <typename T>
template <typename OStream>
void Foo<T>::print(OStream & o)
const
{
    print(o, &default_print_function <OStream>);
}

#include <iostream>

int main()
{
    Foo <int> foo;

    foo.print(std::cerr, &Foo<int>::default_print_function<std::ostream>);
    std::cerr << std::endl;

    foo.print(std::cerr);
    std::cerr << std::endl;
}

However, if I comment out the void print (OStream &) const overload:

template <typename T>
struct Foo
{
    template <typename OStream>
    static void default_print_function(OStream &, const T &);

    template <typename OStream, typename PrintFunction>
    void print(
        OStream &,
        const PrintFunction & = &Foo<T>::default_print_function<OStream>) const;

    template <typename OStream>
    void print(OStream &) const;
};


template <typename T>
template <typename OStream>
void Foo<T>::default_print_function(OStream & o, const T & value)
{
    o << value;
}

template <typename T>
template <typename OStream, typename PrintFunction>
void Foo<T>::print(OStream & o, const PrintFunction & f)
const
{
    T test_value = 123;

    o << "TEST: ";
    f(o, test_value);
}

/*template <typename T>
template <typename OStream>
void Foo<T>::print(OStream & o)
const
{
    print(o, &default_print_function <OStream>);
}*/

#include <iostream>

int main()
{
    Foo <int> foo;

    foo.print(std::cerr, &Foo<int>::default_print_function<std::ostream>);
    std::cerr << std::endl;

    foo.print(std::cerr);
    std::cerr << std::endl;
}

Then it won't compile:

test.cpp: In function ‘int main()’:
test.cpp:54:25: error: no matching function for call to ‘Foo<int>::print(std::ostream&)’
  foo .print (std :: cerr);
                         ^
test.cpp:8:7: note: candidate: template<class OStream, class PrintFunction> void Foo<T>::print(OStream&, const PrintFunction&) const [with OStream = OStream; PrintFunction = PrintFunction; T = int]
  void print (
       ^~~~~
test.cpp:8:7: note:   template argument deduction/substitution failed:
test.cpp:54:25: note:   couldn't deduce template parameter ‘PrintFunction’
  foo .print (std :: cerr);

The default value for the second argument for print is spelled in a way which seems to me to be exactly equivalent to the way it is spelled in the first invocation in the main function.

foo .print (std :: cerr, & Foo <int> :: default_print_function <std :: ostream>);

So why can the template argument be deduced in this invocation, but not be deduced in the default argument

const PrintFunction & = & Foo <T> :: default_print_function <OStream>

?

This is a non-deduced context:

cppreference

Non-deduced contexts:

...

4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done

EDIT (why it is a non-deduced context)

Because non-deduced context has a benefit: it allows the type to be used if it could be obtained from elsewhere.

Consider this example:

template<class T>
void foo(T, T = 1.0) {};

int main() {
    foo(2);
}

This is valid code. But if it were not a non-deduced context, the deduction would conflict. It's somewhat reasonable to assume the programmer want to have a conversion instead of a failure.

On the other hand, the type is essentially given elsewhere (default argument that is given at declaration). For example, your code will be fine if you decltype to the template parameter:

template <typename OStream, typename PrintFunction = decltype(&Foo<T>::default_print_function<OStream>)>

All in all, we lose nothing to make it a non-deduced context. Moreover, we make the conversion of default argument type possible when the type is supplied elsewhere.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM