为什么我需要 `std::type_identity_t` 来启用隐式类型转换?

问题描述 投票:0回答:1
#include <concepts>
#include <format>
#include <string_view>
#include <type_traits>

template <typename ... Args>
struct FmtString {
    std::format_string<Args ...> text;

    template <typename StringCompatible>
    requires (std::convertible_to<StringCompatible, std::string_view>)
    consteval FmtString(StringCompatible const& description)
        : text(description)
        {}
};

template <typename ... Args>
void fails(FmtString<Args ...> const&, Args&& ...) noexcept {}

template <typename... Args>
using FmtStringArgIdentity = FmtString<std::type_identity_t<Args> ...>;

template <typename ... Args>
void works(FmtStringArgIdentity<Args ...> const&, Args&& ...) noexcept {}

int main() {
    works("test {}", 42);
    fails("test {}", 42);
}

GCC 错误消息:

<source>: In function 'int main()':
<source>:28:10: error: no matching function for call to 'fails(const char [8], int)'
   28 |     fails("test {}", 42);
      |     ~~~~~^~~~~~~~~~~~~~~
<source>:18:6: note: candidate: 'template<class ... Args> void fails(const FmtString<Args ...>&, Args&& ...)'
   18 | void fails(FmtString<Args ...> const&, Args&& ...) noexcept {}
      |      ^~~~~
<source>:18:6: note:   template argument deduction/substitution failed:
<source>:28:10: note:   mismatched types 'const FmtString<Args ...>' and 'const char [8]'
   28 |     fails("test {}", 42);
      |     ~~~~~^~~~~~~~~~~~~~~
Compiler returned: 1

查看 libfmt 的源代码,我能够使我的代码工作。不幸的是,我不明白为什么这样做有效,也不明白为什么模板参数推导失败。

fails
调用有什么问题?为什么
works
调用可以解决这个问题?

实时代码

c++ type-conversion c++20 implicit-conversion c++-templates
1个回答
0
投票

具有相同效果的更简单示例:

#include <type_traits>

template <typename T>
struct foo {
    template <typename U>
    foo(U u) {}
};

template <typename T>
void fails(foo<T>,T) {}

template <typename T>
void works(std::type_identity_t<foo<T>>,T) {}

int main()
{
    fails(12.0,42); // error
    works(12.0,42);
}

您无法从

int
推断出
12.0
,从而将其转换为
foo<int>
。用作构造函数模板参数的
double
和用于实例化
int
foo<int>
之间根本没有任何关系。任何
foo<T>
都有一个从
double
foo<T>
的转换构造函数。

std::type_identity
是非推导的上下文。对于
works<T>
T
仅从
42
推导出来,并且
works<int>
被实例化以获得
works<int>(foo<int>,int)
。现在,隐式转换开始生效,并且
12.0
可以通过转换构造函数转换为
foo<int>

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