【问题标题】:MSVC 2012 detecting number of template arguments of a template function via SFINAEMSVC 2012 通过 SFINAE 检测模板函数的模板参数数量
【发布时间】:2015-04-10 10:31:36
【问题描述】:

我正在尝试做的事情:我有一个模板对象传入,作为接口的一部分,它应该有一个定义了许多参数(我不知道有多少)的“进程”函数其中是模板参数。

struct A { static void process(int a); };
struct B { template <typename B0> static void process(int a, B0 b0); };

都是要接收的有效处理程序。所以现在我需要检测处理程序的签名:静态类型参数和一些模板参数。

为此,我使用了许多模板魔术技巧,这些技巧可能会缩小到有问题的部分 - 检测许多模板参数(或仅检索模板签名)。

我试图找出所需信息的方法是使用Is it possible to write a template to check for a function's existence? 中描述的方法检查明确的专用签名

struct _D;

template <typename T>
struct get_template_args_count
{
private:
    template <int count> struct R { enum { value = count }; };

    template <typename C>
    static R<0> retrieve(decltype(&C::process));

    template <typename C>
    static R<1> retrieve(decltype(&C::template process<_D>));

    template <typename C>
    static R<-1> retrieve(...);

public:
    typedef decltype(retrieve<T>(nullptr)) Result;
    enum { value = Result::value };
};

int main(int argc, char* argv[])
{
    std::cout
        << "A::process " << get_template_args_count<A>::value << "\n"
        << "B::process " << get_template_args_count<B>::value << "\n";
    std::cin.get();

    return 0;
}

使用 clang(使用 msvc2013 或 linux 版本构建,使用 gcc-4.9.2 构建)编译并输出:

 A::process 0
 B::process 1

使用 msvc2012 也可以编译,但输出:

 A::process 0
 B::process -1

当通过注释掉后备案例(带有 (...) 的案例)陷入困境时,msvc2012 吓坏了:

main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)'
          with [ T=B, count=1 ]
          With the following template arguments: 'B'
          v:\test\test\test\main.cpp(63) : see reference to class template instantiation 'get_template_args_count<T>' being compiled
          with [ T=B ]
main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)'
          with [ T=B, count=0 ]
          With the following template arguments: 'B'
main.cpp(29): error C2825: 'get_template_args_count<T>::Result': must be a class or namespace when followed by '::'
          with [ T=B ]
main.cpp(29): error C2039: 'value' : is not a member of '`global namespace''
main.cpp(29): error C2275: 'get_template_args_count<T>::Result' : illegal use of this type as an expression
          with [ T=B ]
main.cpp(29): error C2146: syntax error : missing '}' before identifier 'value'
main.cpp(29): error C2143: syntax error : missing ';' before '}'
main.cpp(29): error C2365: 'value' : redefinition; previous definition was 'enumerator'
        main.cpp(29) : see declaration of 'value'

(日志稍微重新格式化以减少行数)

我还尝试使用上述问题的 cmets 中描述的不同技术(使用 char[sizeof]、使用 typeof 并将检查移动到返回类型),但无济于事 - 它要么产生相同的结果,要么分崩离析甚至更奇怪的错误(包括没有明显原因的“意外的文件结束”)。

我还使用另一种技术(通过 SFINAE 比较原型)检查了一个类似的问题 Deduce variadic args and return type from functor template parameter (MSVC-specific),但是当我不知道确切的签名(即我不知道)时,我看不到如何使用它静态参数的数量和类型)。当然,我可能会暴力破解他们手头的特定任务,但是...

所以我有两个问题:

  1. 为什么 MSVC 总是这么难?.. 好吧,这是一个修辞,不需要回答。
  2. 我是否滥用了 clang/gcc 的一些善意,实际上 MSVC 正在做正确的事情,向我抛出无意义的错误?除了暴力破解所有可能的静态/模板参数组合并使用完整的签名原型进行比较之外,是否有任何解决方法或正确的方法来做到这一点?

【问题讨论】:

    标签: c++ templates c++11 visual-c++ sfinae


    【解决方案1】:

    这里的问题是 MSVC 拒绝提取 &amp;C::template process&lt;_D&gt; 的类型以用于重载解决方案(注意没有任何有意义的错误消息)。似乎它认为实例化函数是只有一个函数的重载集;可能是实施失误。

    您可以通过将重载集输入函数参数来强制它将重载集转换为函数指针类型:

    template<typename T>
    T* fn_id(T* func) {
        return nullptr;
    }
    

    一旦T 被扁平化为函数类型,您就可以将其用于decltype

    template <typename C>
        static R<1> retrieve(decltype(fn_id(&C::template process<_D>)));
    

    通过这个 hack,我得到了与使用 clang 相同的输出。

    【讨论】:

    • 我尝试使用蛮力方法,以防万一,并且对带有候选检查无模板的 process(int, T) 函数的“模糊调用”错误感到惊讶进程(int,int)和进程(int,T)。这意味着 - 它试图不是从调用中推断模板参数,而是从转换为指向函数的指针。在上面的检查中缺少“模板”关键字似乎也可以(其他编译器没有)。这一切都支持你的理论——听起来像是一个 MS 延迟模板推导工作。但是你的想法很有效,非常感谢!
    • 附带说明:它也适用于 clang,因此在考虑 msvc2012 时它可以用作交叉编译器解决方案。并且不需要 fn_id 实现。
    • 注意fn_id的定义可以省略。只需要声明fn_id
    猜你喜欢
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    相关资源
    最近更新 更多