具有适当可转换元素的任何可迭代输入的范围参数

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

我目前正在努力适应 C++20,尤其是在这种情况下的概念和范围。希望标题适合我的问题我还不确定我遇到了什么。

我想为我的

class AttribueMap
创建一个 set 方法,以将任何具有可转换为
string
的基础类型的可迭代输入传递给我的班级。班级正在举行简单的
map< string, vector< string >>
。 我使用的概念如下:

template< typename input_data_t, typename range_element_t >
concept is_range_with_elements_of_type = std::ranges::input_range< input_data_t > 
&& std::convertible_to< std::ranges::range_value_t< input_data_t >, range_element_t >;

当我通过

vector< string >
array< string >
甚至
vector< const char* >
它有效。但是
initializer_list< const char* >
static constexpr array< string_view >
不起作用。

我感觉它是关于右值类型的,那么我怎样才能设置 set 方法的约束来处理这种类型的临时输入呢?


归结代码:

#include <ranges>
#include <concepts>
#include <string>
#include <vector>
#include <map>
#include <string_view>

template< typename input_data_t, typename range_element_t >
concept is_range_with_elements_of_type = std::ranges::input_range< input_data_t > && std::convertible_to< std::ranges::range_value_t< input_data_t >, range_element_t >;

class AttributeMap
{
    public:
    using attribute_name_t = std::string;
    using attribute_element_t = std::string;
    using attribute_data_t = std::vector< attribute_element_t >;

    template< typename input_data_t >
    requires is_range_with_elements_of_type< input_data_t, attribute_element_t >
    bool setAttribute(const attribute_name_t&, const input_data_t&)
    {
        if constexpr (std::convertible_to< input_data_t, attribute_element_t>)
        {
            //currently not implemented
            return false;
        }
        else if constexpr (is_range_with_elements_of_type< input_data_t, attribute_element_t >)
        {
            //currently not implemented
            return false;
        }

        return false;
    }

private:
    std::map< attribute_name_t, attribute_data_t > m_data;
};

int main()
{
    AttributeMap dut;

    dut.setAttribute("Friends", { "Chery", "Jack", "Nguyen" }); //error
    dut.setAttribute(std::string("other Friends"), std::array<std::string, 4>{ "Milo of Croton", "Cleopatra", "300", "..." });
    dut.setAttribute(std::string("even more Friends"), std::vector<const char*>{ "Karl", "Gilgamesh" });
    {
        const std::string attribute{ "books" };
        const std::vector< std::string > attributeData{ "Book1", "Book2" };
        dut.setAttribute(attribute, attributeData);
    }
    {
        const std::string attribute{ "Cloths" };
        const std::array< std::string, 5 > attributeData{ "Shirt", "Pullover", "Jeans", "Cap", "Sneaker" };
        dut.setAttribute(attribute, attributeData);
    }
    {
        const std::string attribute{ "Comments" };
        static constexpr std::array< std::string_view, 3 > attributeData{ "great", "rofl u said lol", " . " };
        dut.setAttribute(attribute, attributeData); //error
    }
}

我使用 msvc (14.36.32502) 得到的错误是:

Error C2672 'AttributeMap::setAttribute': 找不到匹配的重载函数

第 11 行:

dut.setAttribute("Friends", { "Chery", "Jack", "Nguyen" });

最后一个

dut.setAttribute(attribute, attributeData);

在第 27 行,使用

static constexpr std::array< std::string_view, 3 >
.

c++ c++20 c++-concepts rvalue std-ranges
2个回答
1
投票

dut.setAttribute("Friends", { "Chery", "Jack", "Nguyen" });

你有一个braced-init-list

{ "Chery", "Jack", "Nguyen" }

可用于列表初始化对象,但您已将其留给模板参数推导来确定应该初始化什么类型的对象

input_data_t

编译器有一个braced-init-list准备好初始化一个对象但是没有关于初始化什么类型的信息,所以编译失败。

您可以在调用站点指定对象的类型,例如

std::initializer_list<const char*>

dut.setAttribute("Friends", 
                 std::initializer_list<const char*>{"Chery", "Jack", "Nguyen"});

或添加一个重载,它采用

std::initializer_list
元素可转换为
attribute_element_t

#include <initializer_list>

template<typename input_data_t>
requires is_range_with_elements_of_type<std::initializer_list<input_data_t>,
                                        attribute_element_t>
bool setAttribute(const attribute_name_t& an,
                  std::initializer_list<input_data_t> il)
{
    //...
}

来自

std::initializer_list

std::initializer_list
对象在以下情况下自动构造:

  • a braced-init-list 用于列表初始化一个对象,其中相应的构造函数接受一个
    std::initializer_list
    参数
  • a braced-init-list 用作赋值的右操作数或函数调用参数,对应的赋值运算符/函数接受一个
    std::initializer_list
    参数
  • a braced-init-list 绑定到
    auto
    ,包括在远程
    for
    循环中
    这意味着这将神奇地起作用:
    auto il = {"Chery", "Jack", "Nguyen"};
    static_assert(std::is_same_v<decltype(il),
                  std::initializer_list<const char*>>);
    dut.setAttribute("Friends", il);
    
    ...但不会像在您的代码中那样跳过绑定到
    auto

0
投票

但是

initializer_list< const char* >
或者一个
static
constexpr array< string_view >
不起作用。

{}
不参与模板推导,所以编译器不知道
{ "Chery", "Jack", "Nguyen" }
的类型。您可以显式指定类型或为
setAttribute

添加默认模板参数
template<typename input_data_t = std::initializer_list<attribute_element_t>>
  requires is_range_with_elements_of_type< input_data_t, attribute_element_t>
bool setAttribute(const attribute_name_t&, const input_data_t&);

对于

array<string_view, N>
,问题是
string_view
不能隐式转换为
string
,这意味着下面的断言失败

static_assert(std::convertible_to<std::string_view, std::string>); // failed

这使得约束不满足。解决方法之一是使用

constructible_from
而不是
convertible_to
来组成
is_range_with_elements_of_type
概念

template<typename input_data_t, typename range_element_t>
concept is_range_with_elements_of_type = 
  std::ranges::input_range<input_data_t> && 
  std::constructible_from<range_element_t, std::ranges::range_value_t<input_data_t>>;

在这种情况下,您需要显式调用构造函数来构造

string
.

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