防止隐式模板实例化
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)
(此行为在 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
个参数:隐式转换 (例如 float
到 int
) 是不允许的。如果这不是您想要的,请考虑 TemplateRex 的回答。
(使用上述约束函数,调用 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();
我仍然想知道为什么这是必要的。估计是国共那些聪明人的优化吧。
在这样的方法重载情况下:
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)
(此行为在 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
个参数:隐式转换 (例如 float
到 int
) 是不允许的。如果这不是您想要的,请考虑 TemplateRex 的回答。
(使用上述约束函数,调用 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();
我仍然想知道为什么这是必要的。估计是国共那些聪明人的优化吧。