首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么initializer_list不被推断为它匹配的概念的一个论点?

为什么initializer_list不被推断为它匹配的概念的一个论点?
EN

Stack Overflow用户
提问于 2022-09-02 19:42:46
回答 2查看 97关注 0票数 0

我在玩概念(我想给这个问题一个C++ey的答案),发现了std::initializer_list的一种令我困惑的行为。尽管以下代码有效:

代码语言:javascript
运行
复制
#include<utility>
#include <limits>
#include<iostream>
#include <type_traits>
#include <vector>
#include <initializer_list>
#include <string_view>

template <class C>
struct iterable_sfinae
{
    template <class U>
    static auto check(U*u)->decltype(u->begin()!=u->end());

    template <class...>
    static void check(...);

    static constexpr bool value = !std::is_void_v<decltype(check((C*)0))>;
};

template <class C> 
static constexpr bool is_iterable_v = iterable_sfinae<C>::value;

template <class C> 
concept Iterable = iterable_sfinae<C>::value;

template <class N>
constexpr bool is_numeric_v = std::numeric_limits<std::decay_t<N>>::is_specialized;

template <class N>
concept Number = std::numeric_limits<std::decay_t<N>>::is_specialized;

// alternative 2
template <Iterable C>
auto findmax(C const &c)
requires is_numeric_v<decltype(*(c.begin()))>
{
   auto rv = std::numeric_limits<std::decay_t<decltype(*(c.begin()))>>::lowest();
   for (auto e: c)
       if (e > rv) rv = e;
   return rv;
}




int main() {
        
    std::vector<int> v = {9, 255, 86, 4, 89, 6, 1, 422, 5, 29};
    std::cout << "using vector via concept: " << findmax(v) << '\n';
    
    std::initializer_list<int> il = {9, 255, 86, 4, 89, 6, 1, 422, 5, 29};
    std::cout << "using initializer_list: " << findmax(il) << '\n';

    std::boolalpha(std::cout);
    std::cout << "initializer_list is iterable: " << is_iterable_v<std::initializer_list<int>> << '\n';
}

产出:

代码语言:javascript
运行
复制
using vector via concept: 422
using initializer_list: 422
initializer_list is iterable: true

但如果我想

代码语言:javascript
运行
复制
// alternative 3
std::cout << "using braced-init-list: " << findmax({9, 255, 86, 4, 89, 6, 1, 422, 5, 29}) << '\n';

程序不会编译。MSVC、gcc和clang给出的理由是“无法推断模板参数”。但是,如果您为std::initializer_list添加了重载,则它会:

代码语言:javascript
运行
复制
template <Number N>
auto findmax(std::initializer_list<N> const &c)
{         
   auto rv = std::numeric_limits<std::decay_t<decltype(*(c.begin()))>>::min();
   for (auto e: c)
       if (e > rv) rv = e;
   return rv;
}

输出:

代码语言:javascript
运行
复制
using braced-init-list: 422

现在,cppreference说(在列表)

当.大括号内列表是一个函数调用参数,而相应的赋值运算符/函数接受一个std::initializer_list参数时,就会自动构造一个std::initializer_list对象。

initializer_list与Iterable概念相匹配,但是基于概念的重载确实接受显式声明的initializer_list变量。,这对我来说意味着,裸露的大括号列表应该生成一个iniitalizer_list,并选择基于概念的重载。但事实并非如此,基于概念的重载可以接受std::initializer_list参数,那么为什么重载不能从带大括号的列表中推断和创建一个initializer_list参数呢?

完整的代码在这里

所以我找到了一个解决办法来强调这一切是多么的奇怪。如果我们将findmax专门化的initializer_list替换为

代码语言:javascript
运行
复制
template <class C>
inline std::initializer_list<C> const &make_il(std::initializer_list<C> const &i)
{
    return i;
}

我们可以在主模板中提示接受

代码语言:javascript
运行
复制
// alternative 3
std::cout << "using make_il: " << findmax(make_il({9, 255, 86, 4, 89, 6, 1, 422, 5, 29})) << '\n';

从大括号内列表创建initializer_list的“魔力”似乎只发生在明确提到initializer_list时,而不是在查找泛型模板参数时(即使它受到约束)。为什么会这样呢?

EN

回答 2

Stack Overflow用户

发布于 2022-09-02 20:11:44

表达式{9, 255, 86, 4, 89, 6, 1, 422, 5, 29}而不是隐式为std::initializer_list

它的意义和类型是从它的用法中推断出来的。

  • 如果将它传递给一个期望std::initializer_list<int>的函数,那么它就是这样的。
  • 如果将它传递给一个期望std::array<int,10>的函数,那么它就是这样的。
  • 如果将它传递给一个期望聚合类型的函数,那么它就是这样的。

但是,您将它作为推导出的模板参数template <Iterable C> auto findmax(C const &c)传递。

findmax({9, 255, 86, 4, 89, 6, 1, 422, 5, 29})不会编译,因为函数依赖于调用站点来指定要传递的类型,而您的调用站点依赖于该函数来指定要传递的类型。

在这两个位置都没有提到std::initializer_list,所以编译器不知道您需要std::initializer_list

票数 3
EN

Stack Overflow用户

发布于 2022-09-02 20:47:52

{..}没有类型,只能推导为const T(&)[N]或(const of) std::initializer_list<T>

期望来自findmax的类型不是上述形式,它接受const T& template <Iterable C> auto findmax(C const &c)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73587304

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档