代码之家  ›  专栏  ›  技术社区  ›  yo_gdg

C++模板元函数

  •  3
  • yo_gdg  · 技术社区  · 7 年前

    我正在为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 *)'

    即使intellisense在“调用”时正确解析 p2(.....) (int, wchar_t*, const wchar_t*)

    你知道这个代码有什么问题吗?

    5 回复  |  直到 7 年前
        1
  •  6
  •   Justin    7 年前

    请注意 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"") ,而不必指定要调用哪个函数。

        2
  •  3
  •   max66    7 年前

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

    但更好的解决方案(IMHO)是Justin(和Klaus,第(2)点)提出的:两个同名函数;参数选择正确的参数。

        3
  •  3
  •   Klaus    7 年前

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

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

    2) 如果表达式的类型在编译时是明确的,则不需要手动选择要调用的函数。这是从编译器中完成的!一个简单的重载非常适合这种情况。根本不需要模板,也不需要SFINAE或constexpr if。

    3) 如果您有用户输入,则无法确定constexpr/std::是否相同,因为所有模板参数都必须在编译时已知。您不能将运行时数据放入模板参数!

    因此,必须有一个完全不同的解决方案为您的问题!

        4
  •  1
  •   R Sahu    7 年前

    你知道这个代码有什么问题吗?

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

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

    E、 g。

    struct A {};
    struct B {};
    
    bool v = true;
    (v ? A() : B());
    

    将导致与nether相同的编译器错误 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();
    };
    
        5
  •  0
  •   Ryan Haining    7 年前

    我不相信您需要这么荒谬的东西,可能只需要重载一个函数即可:

    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{} ,它无效,因为 A B 不兼容。

    有不同的方法可以解决这个问题,对于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();
    }