C++模板元函数
C++ Template Metafunction
我正在为Win32开发一个基于SQL ODBC API的包装器,并且经常具有GetXXXTextA
和GetXXXTextW
等多个功能。我根据用户输入类型选择合适的GetA
或GetW
。我试过这个:
// test getterA
int _stdcall pruebaA (int, char*, const char*)
{ return 0; }
// test getterW
int _stdcall pruebaW(int, wchar_t*, const wchar_t*)
{ return 0; }
template<typename T>
struct only_char_or_wchar_t
{
using ct = std::enable_if_t<std::is_same<T, char>::value || std::is_same<T, wchar_t>::value, T>;
};
template<typename char_type> struct char_or_wchart_api: only_char_or_wchar_t<char_type>
{
constexpr static std::conditional_t<std::is_same<char_type, wchar_t>::value, int (_stdcall*)(int, wchar_t*, const wchar_t*) , int(_stdcall*)(int, char*, const char*)> prueba =
std::is_same<char_type, wchar_t>::value
?
::pruebaW :
::pruebaA;
};
int main () {
auto p2 = char_or_wchart_api<wchar_t>::prueba;
p2(0, nullptr, L"");
return 0;
}
但是,Visual Studio 2017一直在抱怨(在"::pruebaA;
"行):
Error C2446: ':': no conversion from 'int (__stdcall *)(int,char *,const char *)' to 'int (__stdcall *)(int,wchar_t *,const wchar_t *)'
即使智能感知在"调用"p2(.....)
时正确解析(int, wchar_t*, const wchar_t*)
你知道这段代码可能有什么问题吗?
请注意,pruebaA
和pruebaW
具有不同的类型:
int _stdcall pruebaA(int, char*, const char*)
int _stdcall pruebaW(int, wchar_t*, const wchar_t*)
第一个函数采用与第二个函数不同的参数类型,因此这两种函数类型不兼容。不能从三元返回指向两者的指针,因为三元中的两种类型都必须兼容。
但是,您过于复杂了。只需编写一个重载函数:
// Choose better names for the arguments
int prueba(int arg1, char* arg2, const char* arg3) {
return pruebaA(arg1, arg2, arg3);
}
int prueba(int arg1, wchar_t* arg2, const wchar_t* arg3) {
return pruebaW(arg1, arg2, arg3);
}
这也简化了用法,因为您只需要编写prueba(0, nullptr, L"")
,而不必指定要调用的函数。
正如Klaus(以及Justin和R Sahu)所解释的那样,您的三元运算符会收到两个不兼容的对象。
但是,如果您使用模板专用化,则不需要only_char_or_wchar_t
并且(可能使用auto
作为类型)都变得更加简单
template <typename>
struct char_or_wchart_api;
template <>
struct char_or_wchart_api<char>
{ static constexpr auto prueba = ::pruebaA; };
template <>
struct char_or_wchart_api<wchar_t>
{ static constexpr auto prueba = ::pruebaW; };
但更好的解决方案(恕我直言)是贾斯汀提出的解决方案(和克劳斯,第 (2) 点):两个同名的函数;参数选择正确的一个。
我相信你有很多错误的假设!
1)条件/三元运算符不能有两种不同的返回类型。因此,这两个表达式必须是相同的类型,或者必须能够隐式转换为第一个表达式!
2) 如果你的表达式类型在编译时是明确的,你不需要手动选择应该调用的函数。这是从编译器完成的!简单的重载非常适合这种情况。根本不需要模板,也不需要 SFINAE 或 constexpr 如果。
3)如果你有用户输入,你不能用constexpr/std::is_same来决定,因为所有的模板参数都必须在编译时知道。您不能将运行时数据放入模板参数中!
因此,您的问题必须有一个完全不同的解决方案!
你知道这段代码可能有什么问题吗?
该问题是由定义pruebaA
时的条件表达式引起的。
条件表达式的第二项和第三项不能完全不相关。
例如
struct A {};
struct B {};
bool v = true;
(v ? A() : B());
将导致相同的编译器错误,因为下界A
可转换为B
B
也不能转换为A
。
在您的情况下,引起问题的两种类型是int (__stdcall *)(int,char *,const char *)
和int (__stdcall *)(int,wchar_t *,const wchar_t *)
.
您可以使用另一个元函数来帮助实现您的意图。
template <typename T> struct func_selector;
template <> struct func_selector<char>
{
using type = int(*)(int, char*, const char*);
constexpr static type get() { return pruebaA; }
};
template <> struct func_selector<wchar_t>
{
using type = int(*)(int, wchar_t*, const wchar_t*);
constexpr static type get() { return pruebaW; }
};
和使用
template<typename char_type> struct char_or_wchart_api: only_char_or_wchar_t<char_type>
{
constexpr static auto prueba = func_selector<char_type>::get();
};
我不相信你需要这么荒谬的东西,并且可能只是重载一个函数来解决这个问题:
char* GetXXXTextA(char*);
wchar_t* GetXXXTextW(wchar_t*);
// your overloads, same name
char* GetXXXText(char* arg) { return GetXXXTextA(arg); }
wchar_t* GetXXXText(wchar_t* arg) { return GetXXXTextW(arg);
如果您坚持使用 TMP,则问题出在三元上,最后两个操作数必须可转换为相同的类型。对于一个更简单的示例,请考虑以下情况:
#include <type_traits>
struct A { };
struct B { };
int main() {
auto result = std::is_same<int, int>{} ? A{} : B{};
}
虽然在编译时可以清楚地看到这就像有A result = A{}
,但它是无效的,因为A
和B
不兼容。
对于 C++11,您可以使用标记调度在编译时选择重载:
struct A { };
struct B { };
A f(std::true_type) {
return {};
}
B f(std::false_type) {
return {};
}
int main() {
auto result = f(std::is_same<int, int>{});
}
对于 C++17,您可以使用if constexpr
:
#include <type_traits>
struct A { };
struct B { };
auto f() {
if constexpr (std::is_same<int, int>{}) {
return A{};
} else {
return B{};
}
}
int main() {
auto result = f();
}
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗