给定默认值时,为什么此模板参数推导失败?

时间:2018-10-30 13:07:27

标签: c++ templates

以下代码有效:

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

但是,如果我注释掉void print (OStream &) const重载:

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

然后它将无法编译:

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

print的第二个参数的默认值的拼写方式与我在main函数的第一次调用中的拼写方式完全相同。

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

那么为什么不能在此调用中推导出template参数,而在默认参数中推导出

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

1 个答案:

答案 0 :(得分:3)

这是一个非推论上下文:

cppreference

  

非推论上下文:

     

...

     

4)在函数的参数类型中使用的模板参数   具有调用中使用的默认参数的参数   为此进行论证推论

编辑(为什么它是非推论上下文)

因为非推导上下文有一个好处:如果可以从其他地方获取类型,则可以使用该类型。

考虑以下示例:

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

int main() {
    foo(2);
}

这是有效的代码。但是,如果不是非推论的背景,则推论会发生冲突。假设程序员希望进行转换而不是失败是合理的。

另一方面,类型实际上是在其他位置给出的(在声明时给出默认参数)。例如,如果您decltype使用模板参数,则代码会很好:

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

总而言之,我们没有付出任何代价使其成为一个不可推论的背景。此外,当在其他地方提供默认参数类型时,我们可以进行默认参数类型的转换。