可变参数模板中类型推导的混乱

问题描述 投票:0回答:1

我有一个具有多个构造函数的类,一个是可变参数,一个在可变参数之前接受一个额外的可调用函数。但是,我创建的对象会导致我不理解的重载解析。这是我的片段:

#include <iostream>
#include <functional>

struct Test 
{
    std::function<void()> func;
    
    template<class... Args>
    Test(Args&&... args)
    {
        std::cout << "Variadic" << std::endl;
    }
    
    template<class... Args>
    Test(decltype(func) newfunc, Args&&... args)
    {
        std::cout << "Variadic with a callable" << std::endl;
    }
};

int main()
{

    auto l = [](){};
    
    Test t{1,2,3}; //As expected
    
    Test t2{l, 1,2,3}; //Not as expected
    
    Test t3{{l}, 1,2,3}; //As expected, but why???
    
    //Same test, just with temporary
    Test t4{[](){}, 1,2,3}; 
    
    Test t5{{[](){}}, 1,2,3}; 
    
    return 0;
}

该程序(gcc 和 MSVC)的输出是

可变参数

可变参数

带有可调用的可变参数

可变参数

带有可调用的可变参数

第一个调用非常有意义,但我希望调用 2 会导致调用可调用,因为 std::function 对象可以从 lambda 创建。然而,事实并非如此,但是一旦我在情况 3 中将 lambda 包装在另一对大括号中,实质上将其转换为具有 1 个元素的初始化器_列表,就会调用正确的构造函数。我不明白的是为什么情况 2 导致选择第二个重载以及为什么将其设为初始化列表会改变此行为。

c++ templates lambda initializer variadic
1个回答
0
投票

lambda 不是

std::function
,它是 convertible_to 1。

当编译器看到以下形式的可变参数函数时

template<class... Args>
    Test(Args&&... args)
...
template<class... Args>
    Test(std::function<void()> newfunc, Args&&... args)
...
Test t4{[](){}, 1,2,3}; 

第一个非常适合,它不需要任何强制转换,第二个需要从 lambda 到

std::function
的强制转换,因此第一个是通过重载决策选择的。

在第三种情况下,括号

{l}
正在调用构造函数,然后将结果传递给函数,因此编译器必须在执行ADL之前弄清楚首先调用哪个构造函数,因此它决定调用
std::function
构造函数,然后执行 ADL,这会导致第二个函数非常适合,因为它比函数 1 更专业并且不需要强制转换。

你可以通过应用一个概念来打破这个问题。

#include <iostream>
#include <functional>
#include <concepts>


template <typename T>
concept NotConvertibleTofunc = !std::convertible_to<T,std::function<void()>>;

struct Test 
{
    std::function<void()> func;

    template<NotConvertibleTofunc Arg, class... Args>
    Test(Arg&& arg, Args&&... args)
    {
        std::cout << "Variadic" << std::endl;
    }
    
    template<class... Args>
    Test(decltype(func) newfunc, Args&&... args)
    {
        std::cout << "Variadic with a callable" << std::endl;
    }
};

int main()
{

    auto l = [](){};
    
    Test t{1,2,3}; //As expected
    
    Test t2{l, 1,2,3}; //Not as expected
    
    Test t3{{l}, 1,2,3}; //As expected, but why???
    
    //Same test, just with temporary
    Test t4{[](){}, 1,2,3}; 
    
    Test t5{{[](){}}, 1,2,3}; 
    
    return 0;
}
Variadic
Variadic with a callable
Variadic with a callable
Variadic with a callable
Variadic with a callable
© www.soinside.com 2019 - 2024. All rights reserved.