Моя цель — создать свой аналог std::basic_string, но с некоторыми дополнительными условиями. Я хочу, чтобы мой AnyString<CharType, Traits> можно было преобразовать из std::basic_string<CharType, AnyOtherTraits, AnyAlloc>, но я хочу отключить этот конструктор для некоторого CharType, такого, что basic_string<CharType> не существует (компилируется).
Я пытался сделать что-то вроде этого:
template<typename OtherTraits, typename Alloc, typename =
std::enable_if_t<!std::is_array_v<char_type> &&
std::is_trivial_v<char_type> &&
std::is_standard_layout_v<char_type>>>
AnyString(const std::basic_string<char_type, OtherTraits, Alloc>&);
И у меня есть ColouredChar, который не соответствует условиям, указанным внутри enable_if_t.
Теперь, когда я пытаюсь вызвать отключенный конструктор:
std::basic_string<ColouredChar> de("string"_purple);
ColouredString d(de);
Я получаю не только ошибки компиляции от basic_string, но и очень странную, сообщающую мне, что совершенно другой конструктор ЧАСТНОГО конструктора не может преобразовать свой параметр из basic_string.
Есть ли способ сделать эти ошибки компиляции более читабельными? Или хотя бы объяснить, есть ли здесь повод для беспокойства.
Я рассматривал концепции, но понятия не имею, как их здесь применить.
На самом деле ваше требование странное, вы создаете std::basic_string<ColouredChar> de("string"_purple); так оно и есть, но вы думаете, что его не существует?
Базовый пример ограничения конструктора с использованием концепций (а не ваших трейтов)
#include <type_traits>
#include <string>
// declare your own concept
template<typename type_t>
concept my_concept = std::is_convertible_v<type_t, std::string>; // just a demo concept
class ColouredString
{
public:
// then you can limit your constructor to types satisfying that concept
ColouredString(const my_concept auto& /*arg*/)
{
}
~ColouredString() = default;
};
int main()
{
// ColouredString str{ 1 };
ColouredString str{ "hello world!" };
return 0;
}
Просто чтобы указать, что в дополнение к существующему ответу вам не нужно определять концепцию, чтобы использовать ее в предложении require
class ColouredString{
public:
template<typename T>
requires (std::is_convertible_v<T, std::string>)
ColouredString(const T&){}
};
И уже есть концепция std::convertable_to
class ColouredString{
public:
ColouredString(const std::convertible_to<std::string> auto&){}
};
Кстати, раз ты сказал
Я хочу отключить этот конструктор для некоторого CharType, так что basic_string не существует
Ваш код не работает с конструктором string, вероятно, просто потому, что вы пытаетесь его создать. это не имеет ничего общего с ColouredString
std::basic_string<ColouredChar> de("string"_purple); // it already fail here
ColouredString d(de);
Ты прав. Мы не можем инициализировать ColoredString без создания std::basic_string с помощью описанного конструктора. std::basic_string сам генерирует ошибки компиляции, когда мы пытаемся его создать. Но проблема была в нечитаемости текстов ошибок компиляции - об этом я и спрашивал. Спасибо.
@CapyMaths, тогда ограничение на конструктор ColouredString не поможет.
Вы можете попробовать использовать концепции, которые обычно дают более понятную диагностику; однако это слишком широко и расплывчато для краткого ответа.