防止隐式模板实例化

Prevent implicit template instantiation

在这样的方法重载情况下:

struct A
{
  void foo( int i ) { /*...*/ }
  template<typename T> void foo( T t ) { /*...*/ }
}

除非明确命令,否则如何防止模板实例化?:

A a;
a.foo<int>( 1 ); // ok
a.foo<double>( 1.0 ); // ok
a.foo( 1 ); // calls non-templated method
a.foo( 1.0 ); // error

谢谢!

您可以引入一个 depedent_type 结构来防止 template argument deduction.

template <typename T>
struct dependent_type
{
    using type = T;
};

struct A
{
  void foo( int i ) { /*...*/ };
  template<typename T> void foo( typename dependent_type<T>::type t ) { /*...*/ }
}

在你的例子中:

a.foo<int>( 1 );      // calls the template
a.foo<double>( 1.0 ); // calls the template
a.foo( 1 );           // calls non-templated method
a.foo( 1.0 );         // calls non-templated method (implicit conversion)

wandbox example

(此行为在 cppreference > template argument deduction > non-deduced contexts 上有解释。)


如果想让a.foo( 1.0 )编译出错,需要约束第一个重载:

template <typename T> 
auto foo( T ) -> std::enable_if_t<std::is_same<T, int>{}> { }

此技术使 foo 的上述重载仅采用 int 个参数:隐式转换 (例如 floatint 是不允许的。如果这不是您想要的,请考虑 TemplateRex 的回答。

wandbox example

(使用上述约束函数,调用 a.foo<int>( 1 ) 时两个重载之间会发生奇怪的交互。我 因为我不确定底层指导它的规则。)

到目前为止,执行所需操作的最简单方法是显式删除不需要的重载:

void foo(double) = delete;

即有以下明确的例子:

#include <iostream>

struct A
{
    void foo(int) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
    void foo(double) = delete;

    template<typename T> 
    void foo( T ) {std::cout << __PRETTY_FUNCTION__ << "\n"; }
};

int main() 
{    
    A a;
    a.foo<int>( 1 );        // ok
    a.foo<double>( 1.0 );   // ok
    a.foo( 1 );             // calls non-templated method
    a.foo( 1.0 );           // error    
}

main 中的最后一行被注释掉 this prints

void A::foo(T) [with T = int]
void A::foo(T) [with T = double]
void A::foo(int)

最后一行留在 this prints

prog.cc: In function 'int main()':
prog.cc:18:16: error: use of deleted function 'void A::foo(double)'
     a.foo( 1.0 );           // error
                ^
prog.cc:6:10: note: declared here
     void foo(double) = delete;

向锅中添加另一个建议,与 Vittorio 的回答类似,您还可以向签名添加另一个模板参数:

template <class UserType, class InputType>
void foo(InputType x){...}

然后要使用它,您必须指定第一个模板参数,因为它无法推导。这具有能够区分用户想要的和传入的内容的微小优势,这在某些情况下可能会有用。

哈利路拉!!!!!!!经过多年的悲伤,我终于找到了答案。因为我知道 cpp 规则,定义中的单独声明不只是扔在转储中!!!

// declare template
template <class T> void test();

// declare specialization
template <> // very import to start with this token
void<int> test();

// implicit instantiation
template // if you use this instead, you get instantiation
void<int> test();

我仍然想知道为什么这是必要的。估计是国共那些聪明人的优化吧。