gpt4 book ai didi

c++11 - gcc、clang 和 msvc 之间的可变参数模板示例的不同结果 - 谁能解释一下?

转载 作者:行者123 更新时间:2023-12-01 00:40:29 25 4
gpt4 key购买 nike

我需要创建一个函数,该函数采用带有可变参数和一些固定参数的函数指针,并且无法使其在 Visual Studio 2013 上工作。我认为可能 Visual Studio 2013 缺少一些通常是案例的东西并且做了一个最小的示例完成了我需要的操作,并针对 gcc 和 clang 进行了尝试。我在所有三个编译器上都得到了完全不同的结果。所以我想解决的问题是:

  • 我的例子是否有效?如果不是我做错了什么?
  • 如果我的示例有效,是否有任何关于 gcc 和 clang 行为的提示(让我们将 msvc 算在内,因为它是一个黑匣子)?

  • 这个例子:
    #include <iostream>

    struct foo
    {
    void work(int first, int second, int third)
    {
    std::cout << "0: " << first << ",1: " << second << ",2: " << third << std::endl;
    }
    void work_with_double(double first, int second, int third, int fourth)
    {
    std::cout << "0: " << first << ",1: " << second << ",2: " << third << ",3: " << fourth << std::endl;
    }
    };

    template<typename ... argument_types>
    void invoke_foo(foo* instance, int first, int second, int third, void (foo::*method)(argument_types ... arguments, int, int, int), argument_types ... arguments)
    {
    (instance->*method)(arguments ..., first, second, third);
    }

    int main(int argc, char** argv)
    {
    foo instance;
    invoke_foo(&instance, 1, 2, 3, &foo::work); // gcc ok, clang err, msvc 2013 err
    invoke_foo<>(&instance, 1, 2, 3, &foo::work); // gcc ok, clang err, msvc 2013 err
    invoke_foo(&instance, 1, 2, 3, &foo::work_with_double, 1.0); // gcc err, clang ok, msvc 2013 err
    invoke_foo<double>(&instance, 1, 2, 3, &foo::work_with_double, 1.0); // gcc err, clang err, msvc 2013 ok
    return 0;
    }

    使 Visual Studio 2015(无更新)崩溃的修改片段

    invoke_foo作为对象的成员函数,Visual Studio 2015 崩溃。
    #include <iostream>
    #include <memory>

    struct foo
    {
    void work(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eight)
    {
    std::cout << "0: " << first << ",1: " << second << ",2: " << third << std::endl;
    }
    void work_with_double(double firstExtra, int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eight)
    {
    std::cout << "0: " << first << ",1: " << second << ",2: " << third << ",3: " << fourth << std::endl;
    }
    };

    struct bar
    {

    };

    struct wrapper
    {

    template <typename T> struct non_deduced { using type = T; };
    template <typename T> using non_deduced_t = typename non_deduced<T>::type;

    template<typename ... argument_types>
    std::shared_ptr<bar> invoke_foo(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eight, void (foo::*method)(non_deduced_t<argument_types>... arguments, int, int, int, int, int, int, int, int), argument_types ... arguments)
    {
    (foo_.get()->*method)(arguments ..., first, second, third, fourth, fifth, sixth, seventh, eight);
    return nullptr;
    }

    std::unique_ptr<foo> foo_ = std::move(std::unique_ptr<foo>(new foo));

    };

    int main(int argc, char** argv)
    {
    wrapper instance;
    instance.invoke_foo(1, 2, 3, 4, 5, 6, 7, 8, &foo::work);
    instance.invoke_foo(1, 2, 3, 4, 5, 6, 7, 8, &foo::work_with_double, 1.0);
    }

    最佳答案

    每种情况下的问题是编译器试图推断 argument_types来自 method参数,这是非法的,因为可变参数模板参数只能在它们位于参数列表的末尾时进行推断。

    void (foo::*method)(argument_types ... arguments, int, int, int)
    ^^^^^^^^^^^^^^^^^^ can't infer here
    ^^^^^^^^^^^^^^^ because of these
    解决方法是保护 argument_types从在这种情况下推导出来,使用像 identity 这样的助手:
    template<class T> struct identity { using type = T; };
    template<class T> using identity_t = typename identity<T>::type;

    // ...

    template<typename ... argument_types>
    void invoke_foo(foo* instance, int first, int second, int third,
    void (foo::*method)(identity_t<argument_types> ... arguments, int, int, int), argument_types ... arguments)
    // ^^^^^^^^^^^ fix here

    这是您的代码中的错误还是编译器中的错误?实际上,这是编译器中的一个错误(是的,所有这些);问题是出现在函数类型中而不是参数列表末尾的参数包是否是非推导的上下文。标准的相关部分是 [temp.deduct.type] ,其中指出:

    5 - The non-deduced contexts are: [...]

    • A function parameter pack that does not occur at the end of the parameter-declaration-list.

    6 - When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced. However, a compound type can include both deduced and non-deduced types.


    在这里, argument_types在推导 method 的类型时处于非推导上下文中,但是在推导 invoke_foo 的尾随参数的类型时推导的上下文.
    您可以测试的另一个编译器是 ICC(英特尔 C++ 编译器); ICC 拒绝前两种形式而接受后两种形式,这与 gcc 完全相反。编译器的行为如此不同的原因是,处理这种代码本质上是一个错误处理问题,特别是识别模板参数何时出现在非推导上下文中,并使用在其他地方推导的类型。编译器(每个都以自己的方式)认识到 argument_types不能在 method 内推导出来,但没有意识到或接受它可以在其他地方推导出来。
    具体来说,似乎:
  • gcc 假设如果它不能推导出 argument_types来自 method ,必须为空;
  • clang 假设如果 argument_types被推导出为空或明确指定,这一定是一个错误;
  • MSVC 无法让 argument_types 的扣除覆盖推断它的失败,但如果明确指定则可以;
  • ICC 假设如果 argument_types被推导出为空,这一定是一个错误。
  • 关于c++11 - gcc、clang 和 msvc 之间的可变参数模板示例的不同结果 - 谁能解释一下?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37969651/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com