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

标签 c++ type-conversion c++20 implicit-conversion c++-templates

#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 调用可以解决这个问题?

Live-Code

最佳答案

这是一个具有相同效果的更简单的示例:

#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>通过转换构造函数。

关于c++ - 为什么我需要 `std::type_identity_t` 来启用隐式类型转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77529748/

相关文章:

javascript - 为什么 JavaScript 不将类似数组的对象转换为数组?

c++ - 如何从单个字符创建字符串?

C++ 20 计时码表 : How to compare time_point with month_day?

c++ - 正确使用原子

c++ - 是否可以在私有(private)内存空间中分配一个用于 boost 托管共享内存的对象?

c++ - 如果键不存在于 std::map 中,如何有效地从 std::unordered_set 中删除该键?

C++ - 不编译对象 vector 的迭代器

java类型转换问题

c++ - 关于实例化上下文的问题

c++ - 为什么在 C++ 20 中从标准库容器中删除了比较运算符?