C ++模板元功能

时间:2018-04-03 19:57:52

标签: c++ templates winapi c++14 template-meta-programming

我正在为Win32的SQL ODBC API开发包装器,并且常常有/etc/hostsGetXXXTextA这样的函数。我根据用户输入类型选择适当的GetXXXTextWGetA。我试过这个:

GetW

但是,Visual Studio 2017一直抱怨(排在&#34; // 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; } &#34;):

::pruebaA;

即使intellisense在&#34;调用&#34; Error C2446: ':': no conversion from 'int (__stdcall *)(int,char *,const char *)' to 'int (__stdcall *)(int,wchar_t *,const wchar_t *)'p2(.....)

你知道这段代码有什么不妥吗?

5 个答案:

答案 0 :(得分:6)

请注意pruebaApruebaW有不同的类型:

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""),而不必指定要调用的函数。

答案 1 :(得分:3)

我相信你有很多错误的假设!

1)条件/三元运算符不能有两种不同的返回类型。因为这两个表达式必须是相同的类型,或者必须能够隐式转换为第一个!

2)如果表达式的类型在编译时是清除的,则不需要手动选择应该调用的函数。这是从编译器完成的!简单的过载非常适合这种情况。根本不需要模板,没有SFINAE或constexpr if。

3)如果你有用户输入,你不能用constexpr / std :: is_same来决定,因为所有模板参数必须在编译时知道。您不能将运行时数据放入模板参数中!

因此,对你来说必须有一个完全不同的解决方案!

答案 2 :(得分:3)

As explained by Klaus (and Justin, and R Sahu) your ternary operator receive two incompatible object.

But, if you use template specialization, you don't need only_char_or_wchar_t and (maybe using auto for the type) all became simpler

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

But the better solution (IMHO) is the one proposed by Justin (and Klaus, point (2)): two functions with the same name; the arguments selecting the correct one.

答案 3 :(得分:1)

  

你知道这段代码有什么不妥吗?

问题是由定义pruebaA时的条件表达式引起的。

条件表达式的第二项和第三项不能完全不相关。

E.g。

struct A {};
struct B {};

bool v = true;
(v ? A() : B());

将导致相同的编译器错误,因为地址A可转换为BB也无法转换为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();
};

答案 4 :(得分:0)

我不相信你需要一些如此荒谬的东西,并且可能只是重载一个函数来处理这个问题:

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 问题出现在三元组中,那么最后两个操作数必须可以转换为相同的类型。有关更简单的示例,请考虑this

#include <type_traits>
struct A { };
struct B { };

int main() {
  auto result = std::is_same<int, int>{} ? A{} : B{};
}

虽然在编译时可以清楚地看到这就像拥有A result = A{}一样,但它无效,因为AB不兼容。

对此有不同的解决方法,对于C ++ 11,您可以使用tag dispatching在编译时选择重载:

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