You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20中让AttributeMap支持可转换为string的临时可迭代输入

问题描述

正在学习C++20的concepts与ranges特性,希望为AttributeMap类实现setAttribute方法,使其能够接收任何元素可转换为string的可迭代输入。该类内部持有map<string, vector<string>>类型的成员变量。

使用的concept定义如下:

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>类型的临时输入,猜测问题与右值类型有关,需修改约束条件以兼容这类输入。

精简后的测试代码:

#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;
};


#include <array>

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的代码报以下错误:

Error C2672 'AttributeMap::setAttribute': no matching overloaded function found

解决方案

修改点说明

  1. 调整concept约束逻辑:改用requires表达式直接检查元素转换可行性,替代std::convertible_to,兼容更多场景(如const元素引用、string_view)。
  2. 增加initializer_list重载:解决braced-init-list无法被模板推导为input_range的问题,转发到通用可迭代版本处理。
  3. 补充单个元素重载(可选):支持直接传递单个string或const char*,提升API易用性。

修改后的完整代码

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

template<typename input_data_t, typename range_element_t>
concept is_range_with_elements_of_type =
    std::ranges::input_range<input_data_t> &&
    requires(std::ranges::range_reference_t<input_data_t> elem) {
        static_cast<range_element_t>(elem);
    };

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

    // 处理单个string元素
    bool setAttribute(const attribute_name_t& name, const attribute_element_t& value)
    {
        m_data[name] = {value};
        return true;
    }

    // 处理单个const char*元素
    bool setAttribute(const attribute_name_t& name, const char* value)
    {
        return setAttribute(name, attribute_element_t(value));
    }

    // 通用可迭代输入版本
    template<typename input_data_t>
    requires is_range_with_elements_of_type<input_data_t, attribute_element_t>
    bool setAttribute(const attribute_name_t& name, const input_data_t& data)
    {
        attribute_data_t vec;
        // 预分配空间提升性能
        if constexpr (std::ranges::sized_range<input_data_t>)
        {
            vec.reserve(std::ranges::size(data));
        }
        for (const auto& elem : data) {
            vec.emplace_back(elem);
        }
        m_data[name] = std::move(vec);
        return true;
    }

    // 处理initializer_list的重载
    template<typename U>
    requires std::convertible_to<U, attribute_element_t>
    bool setAttribute(const attribute_name_t& name, std::initializer_list<U> data)
    {
        return setAttribute(name, static_cast<const std::initializer_list<U>&>(data));
    }

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

int main()
{
    AttributeMap dut;

    dut.setAttribute("Friends", { "Chery", "Jack", "Nguyen" }); // 正常编译
    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); // 正常编译
    }

    // 测试单个元素输入
    dut.setAttribute("SingleValue", "HelloWorld");
    dut.setAttribute("SingleCStr", "HelloCStr");
}

问题根源解析

  1. initializer_list无法推导:braced-init-list本身不是具体类型,编译器无法将其直接推导为模板参数input_data_t,因此需要专门的重载匹配std::initializer_list。
  2. constexpr array的兼容性:原concept使用std::convertible_to结合range_value_t的方式,在MSVC旧版本中可能存在判定偏差,改用requires表达式直接检查元素转换,避免了这类兼容性问题。

内容的提问来源于stack exchange,提问作者Tobxon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:37:36