Несколько запутался с проверкой типов в SFINAE. Допустим, есть:
class Foo
{
public:
std::string member;
std::string member2;
template <
typename T, // Parameter 1.
typename U, // Parameter 2.
typename Enable = typename std::enable_if <
std::is_constructible<std::string, T>::value &&
std::is_constructible<std::string, U>::value>::type>
Foo(T&& member, U&& member2) :
member{std::forward<T>(member)},
member2{std::forward<U>(member2)}
{}
};
Не пойму, что нужно использовать, std::is_constructible или же std::is_convertible или std::is_same? Про последний, так понимаю жесткое совпадение нам обеспечено, можно еще std::decay только добавить. Однако хотелось бы строку инициализировать С-массивом или, например, вектор через std::initializer_list. Про std::is_convertible знаю, что пропустит, например, преобразование float в int, однако в таком коде:
template<class D>
impl_ptr(pointer p, D&& d,
typename std::enable_if<
std::is_convertible<D, deleter_type>::value,
dummy_t_
>::type = dummy_t_()) noexcept
: ptr_(std::move(p), std::forward<D>(d)) {}
используется (этот пример - конструктор smart-указателя для реализации pimpl, D и deleter_type - это удалители, ptr_ имеет тип std::unique_ptr).
std::is_convertible подразумевает именно и только неявные преобразования, со всеми сопутствующими ограничениями, налагаемыми на неявные преобразования. Он не будет принимать во внимание конвертирующие конструкторы и операторы приведения типа, помеченные как explicit. Он не будет выполнять более одного пользовательского преобразования в последовательности преобразований.
std::is_constructible напрямую рассматривает именно конструкторы, причем независимо то того, помечены ли они как explicit.
То есть разделение тут совершено четкое и [примерно] соответствует разнице между прямой инициализацией T a(b) и инициализацией копированием T a = b. Даже в С++17 различия между этими формами инициализации не сводятся только к explicit.
Выбирайте, что именно вам нужно в вашем случае.
У вас сейчас is_constructible, что фактически вытаскивает наружу все конструкторы std::string, включая конструкцию из аллокатора. Вам нужна такая возможность? Подозреваю, что исходный замысел был в том, чтобы разрешить только конструкцию из разнообразных видов строк. А тогда уместнее именно is_convertible.
В подобных случаях лучше использовать std::is_constructible, потому что вы передаёте аргумент в конструктор, а значит и проверять вам нужно существование конструктора для данных аргументов.
Но для конкретно приведённого примера лучше использовать string_view (если доступен), или принимать стоки по значению и передавать через std::move. Вариант с шаблонной реализацией создаёт больше проблем, чем пользы.
А std::is_convertible для удалителей в умных указателях используется из-за того, что удалителем может быть лямбда, которая неявно преобразуется в указатель на функцию, но указатель на функцию не конструируется напрямую из лямбды.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости