Вывод аргумента шаблона функции с помощью определяемого пользователем оператора преобразования - PullRequest
0 голосов
/ 14 октября 2019

Допустим, у меня есть класс, который переносит строковый литерал:

template <size_t N>
class literal {
public:
    constexpr literal(const char(&s)[N+1]) : wrapped_(s) {}

    constexpr const char * c_str() const { return wrapped_; }
    constexpr size_t size() const { return N; }

private:
    const char (&wrapped_)[N+1];
};

template <size_t N>
literal<N-1> make_literal(const char (&s)[N]) { return literal<N-1>(s); }

Теперь я хотел бы, чтобы экземпляры этого типа переносимой строки были преобразованы обратно в const char[N] неявно, вКстати, я все еще могу получить доступ к его размеру. Я хотел бы иметь возможность сделать что-то вроде:

template <size_t N>
void foo(const char(&s)[N]) {
    std::cout << N << ": " << s << std::endl;
}

int main() {
    constexpr auto s = make_literal("testing");
    foo(s);
}

Моя цель состоит в том, чтобы иметь одну функцию, определенную для foo(), которая может принимать фактические строковые литералы, а также переносимые литералы. Я попытался добавить пользовательский оператор преобразования в определение класса:

using arr_t = char[N+1];    
constexpr operator const arr_t&() const { return wrapped_; }

Но с помощью clang это дает мне следующее:

шаблон кандидата игнорируется: не может быть найдено'const char [N]' против 'const literal <7>'

Если я изменю вызов на foo() на следующее, это будет работать:

foo((const char(&)[8])s);

. .. что означает, что оператор преобразования работает , но не в контексте вывода аргументов шаблона. Есть ли способ, которым я могу сделать эту работу, не определяя foo() специально для получения обернутого литерала?

1 Ответ

0 голосов
/ 14 октября 2019

Проблема, с которой вы сталкиваетесь, заключается в том, что шаблоны никогда не выполняют преобразование параметров. Поскольку вы даете ему const literal<7>, это все, с чем он должен работать.

Простое решение для этого - добавить перегрузку, а затем выполнить приведение в перегрузке, чтобы вызвать вашу строковую буквальную версию. Это должно выглядеть как

template <size_t N>
void foo(const literal<N> &lit) {
    foo(static_cast<typename literal<N>::arr_t&>(lit)); // explicitly cast to the array type alias
}

, что дает вам полный пример

template <size_t N>
class literal {
public:
    constexpr literal(const char(&s)[N+1]) : wrapped_(s) {}

    constexpr const char * c_str() const { return wrapped_; }
    constexpr size_t size() const { return N; }
    using arr_t = const char[N+1];    // <- Add const here since literals are const char[N]
    constexpr operator const arr_t&() const { return wrapped_; }

private:
    const char (&wrapped_)[N+1];
};

template <size_t N>
constexpr literal<N-1> make_literal(const char (&s)[N]) { return literal<N-1>(s); }

template <size_t N>
void foo(const char(&s)[N]) {
    std::cout << N << ": " << s << std::endl;
}

template <size_t N>
void foo(const literal<N> &lit) {
    foo(static_cast<typename literal<N>::arr_t&>(lit)); // explicitly cast to the array type alias
}

int main() {
    constexpr auto s = make_literal("testing");
    foo(s);
}

Да, вы добавляете перегрузку, но весь важный код не должен дублироваться.


Если вы можете использовать C ++ 17 и не возражаете против небольшой косвенности, вы можете сделать все это с помощью одной функции, используя std::string_view и предоставляя literalс operator std::string_view. Это было бы похоже на

template <size_t N>
class literal {
public:
    constexpr literal(const char(&s)[N+1]) : wrapped_(s) {}

    constexpr const char * c_str() const { return wrapped_; }
    constexpr size_t size() const { return N; }
    using arr_t = const char[N+1];    
    constexpr operator std::string_view() const { return wrapped_; }

private:
    const char (&wrapped_)[N+1];
};

template <size_t N>
constexpr literal<N-1> make_literal(const char (&s)[N]) { return literal<N-1>(s); }

void foo(std::string_view s) {
    std::cout << s.size() << ": " << s << std::endl;
}


int main() {
    constexpr auto s = make_literal("testing");
    foo(s);
    foo("testing");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...