用户定义的转换是否不适用于可变函数参数?为什么不呢?

问题描述 投票:3回答:2

我有一个主要包装std::variant的类,其中包含一些次要的附加功能/元数据。

为了简化使用,我想提供此包装器类到底层变量类型的用户定义转换,因此可以直接在其上调用std::holds_alternative之类的函数。

我发现的内容使我非常困惑是否以及何时应用用户定义的转换。这是简化的代码。

#include <iostream>
#include <variant>

struct MyClass
{
    // "MyClass" is implicitly convertible to a variant<bool,int>
    operator std::variant <bool, int> ()
    {
        return std::variant<bool,int>(5);
    }
};

void thisFunctionTakesOneSpecificVariantType (std::variant<bool,int> v)
{
    std::cout << v.index();    
}

template <class... types>
void thisFunctionTakesAnyVariantType (std::variant<types...> v)
{
    std::cout << v.index();
}

int main ()
{
    MyClass mc;

     // 1. This compiles and runs as expected, 
     //    proving the user-defined conversion (MyClass -> variant<int,bool>) exists and works "sometimes"
     thisFunctionTakesOneSpecificVariantType (mc);

     // 2. This compiles and runs as expected,
     //    proving "thisFunctionTakesAnyVariantType" is well defined 
     thisFunctionTakesAnyVariantType (std::variant <bool, int> (5)); 

     // 3. But, combining 1 & 2, this fails to compile:
     /* fails */ thisFunctionTakesAnyVariantType (mc);  // error: no matching function for call to 'thisFunctionTakesAnyVariantType'

     // 4. This is what i really want to do, and it also fails to compile
     /* fails */ std::holds_alternative<int>(mc);       // error: no matching function for call to 'holds_alternative'

     // 5. An explicit conversion works for 3 and 4, but why not an implicit conversion?
     //      After all, the implicit conversion worked in #1
     thisFunctionTakesAnyVariantType ( static_cast<std::variant <bool, int>> (mc) );

   return EXIT_SUCCESS;
}

为什么不使用情况3和4进行编译,而使用1、2和5进行编译?

在错误消息中,提供此注释:

note: candidate template ignored: could not match 'variant<type-parameter-0-1...>' against 'MyClass'
    inline constexpr bool holds_alternative(const variant<_Types...>& __v)
c++ templates implicit-conversion
2个回答
1
投票

为什么不使用案例3进行编译

因为template argument deduction中未考虑隐式转换:

类型推导不考虑隐式转换(上面列出的类型调整除外:这是overload resolution的工作,稍后会发生。

将不考虑从MyClassstd::variant <bool, int>的转换,然后类型推导失败。如#5所示,您可以在传递给thisFunctionTakesAnyVariantType之前应用显式转换。

为什么不使用用例4进行编译

与#3相同。请注意,即使您为参数包指定了一些模板参数,模板参数推导仍会尝试从函数参数中推导以下模板参数。您可以将推论中的函数参数排除为

template <class... types>
void thisFunctionTakesAnyVariantType (std::type_identity_t<std::variant<types...>> v)

然后您可以将其称为

thisFunctionTakesAnyVariantType<bool, int>(mc);

但是请注意,这会使所有模板参数推导无效(并且#2和5将失败,因此这可能不是一个好主意。

BTW:std::type_identity自C ++ 20起就被支持,甚至很容易实现。


1
投票

这就是规则的布局方式。有更多知识的人来找我,也许我会来这里给您提供确切的规则(考虑模板替换和转换),但是最终还是这样,您无法更改。

为了考虑转换,您的课程需要从std::type_identity继承。您所有的示例都将编译。但是,我不愿意推荐这种方法,因为实际上组合似乎是正确的设计。

我将提供一种转换方法。您失去了它的隐含特性,但是也许这不是一个坏主意,至少要考虑其他选择。

std::variant<int, bool>
struct MyClass
{
    std::variant<bool, int> to_var() const
    {
        return {5};
    }
}

您可以将转换运算符留给它可以工作的那些情况,但是请考虑它是否不仅会增加混乱。

© www.soinside.com 2019 - 2024. All rights reserved.