我试图根据类模板参数确定调用哪个版本的成员函数。我试过这个:
#include <iostream>
#include <type_traits>
template<typename T>
struct Point
{
void MyFunction(typename std::enable_if<std::is_same<T, int>::value, T >::type* = 0)
{
std::cout << "T is int." << std::endl;
}
void MyFunction(typename std::enable_if<!std::is_same<T, int>::value, float >::type* = 0)
{
std::cout << "T is not int." << std::endl;
}
};
int main()
{
Point<int> intPoint;
intPoint.MyFunction();
Point<float> floatPoint;
floatPoint.MyFunction();
}
我认为这是说“如果 T 是 int,则使用第一个 MyFunction,如果 T 不是 int,则使用第二个 MyFunction,但我收到编译器错误,说“错误:'struct std::enable_if' 中没有名为 'type' 的类型“。谁能指出我在这里做错了什么?
enable_if
之所以有效,是因为模板参数的替换导致了错误,因此替换会从重载解析集中删除,编译器只考虑其他可行的重载。
在您的示例中,实例化成员函数时不会发生替换,因为当时模板参数
T
是已知的。实现您尝试的最简单方法是创建一个默认为 T
的虚拟模板参数,并使用它来执行 SFINAE。
template<typename T>
struct Point
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
std::cout << "T is not int." << std::endl;
}
};
编辑:
正如 HostileFork 在评论中提到的,原始示例留下了用户为成员函数显式指定模板参数并得到不正确结果的可能性。以下内容应防止编译成员函数的显式特化。
template<typename T>
struct Point
{
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is int." << std::endl;
}
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is not int." << std::endl;
}
};
一个简单的解决方案是使用委托给worker private函数:
template<typename T>
struct Point
{
void MyFunction()
{
worker(static_cast<T*>(nullptr)); //pass null argument of type T*
}
private:
void worker(int*)
{
std::cout << "T is int." << std::endl;
}
template<typename U>
void worker(U*)
{
std::cout << "T is not int." << std::endl;
}
};
当
T
为 int
时,将调用第一个 worker
函数,因为 static_cast<T*>(0)
结果是 int*
类型。在所有其他情况下,将调用模板版本的worker。
我认为这遵循@Praetorian的解决方案,但我发现它更容易:
template<typename T>
struct Point
{
template<typename U = T>
std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, int>::value>
MyFunction()
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, float>::value>
MyFunction()
{
std::cout << "T is not int." << std::endl;
}
};
根据 Praetorian 的建议(但不更改函数的返回类型),这似乎可行:
#include <iostream>
#include <type_traits>
template<typename T>
struct Point
{
template<typename U = T>
void MyFunction(typename std::enable_if<std::is_same<U, int>::value, U >::type* = 0)
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
void MyFunction(typename std::enable_if<!std::is_same<U, int>::value, float >::type* = 0)
{
std::cout << "T is not int." << std::endl;
}
};
int main()
{
Point<int> intPoint;
intPoint.MyFunction();
Point<float> floatPoint;
floatPoint.MyFunction();
}
enable_if
仅适用于 推导 函数模板参数或专用类模板参数。你所做的不起作用,因为显然使用固定的 T = int
,第二个声明是错误的。
这是可以做到的:
template <typename T>
void MyFreeFunction(Point<T> const & p,
typename std::enable_if<std::is_same<T, int>::value>::type * = nullptr)
{
std::cout << "T is int" << std::endl;
}
// etc.
int main()
{
Point<int> ip;
MyFreeFunction(ip);
}
另一种方法是专门针对各种类型
Point
T
,或者将上述自由函数放入嵌套成员模板包装器中(这可能是更“合适”的解决方案)。
下面的点模板只能使用 int 或 float 作为模板参数 T 来实例化。
回答这个问题:这里,worker() 的调用完全取决于 method() 调用的模板参数,但你仍然可以控制类型。
template<typename T>
struct Point
{
static_assert (
std::is_same<T, int>() ||
std::is_same<T, float>()
);
template<typename U>
void method(U x_, U y_)
{
if constexpr (std::is_same<T, U>()) {
worker(x_, y_);
return;
}
// else
worker(
static_cast<T>(x_),
static_cast<T>(y_)
);
return ;
}
private:
mutable T x{}, y{};
void worker(T x_, T y_)
{
// nothing but T x, T y
}
};
上面的worker()当然会工作,即使它被声明为静态。出于某种正当理由。上述内容几乎没有其他扩展是可能的(而且很简单),但让我们坚持答案。