шаблоны по квалификаторам this

ilnur.khuziev
ilnur.khuziev
struct SaveLoadableType {
    int a;
    int b;
    int c;

    template<class IoClass, trait Qualifier>
    void SaveLoad(IoClass& io) Qualifier {
        io.AddField(a);
        io.AddField(b);
        io.AddField(c);
    }
}

struct TObjStreamSaver {
    template<class T>
    AddField(const T& val) {
        cout << val << " ";
    }
}

struct TObjStreamLoader {
    template<class T>
    AddField(T& val) {
        cin >> val;
    }
}

int main() {
    SaveLoadableType object;

    {
        const auto& ref = object;
        TObjStreamSaver saver;
        ref.SaveLoad(saver); //called const specialization: void SaveLoadableType::SaveLoad(TObjStreamSaver & io) const
    }
    
    {
        auto& ref = object;
        TObjStreamLoader loader;
        ref.SaveLoad(loader); //called non-const specialization: void SaveLoadableType::SaveLoad(TObjStreamLoader& io) const
    }
}

 

Конечно, введение нового ключевого слова очень спорно. В качестве алтернативы я бы предложил

struct SaveLoadableType {
    int a;
    int b;
    int c;

    template<class IoClass, class SelfClass>
    void SaveLoad(IoClass& io) SelfClass {
        io.AddField(a);
        io.AddField(b);
        io.AddField(c);
    }
}

Конечно, должно быть автоматическое выведение квалификатора.

 

-3
рейтинг
11 комментариев
dmitriy@izvolov.ru
Действительно, иногда очень хочется иметь возможность пробрасывания (он же perfect forwarding) для *this, чтобы не реализовывать по три метода для класса:
method () &
method () const &
method () &&

Но в представленном виде мне предложение не нравится. Нужно как-то по-другому.
dmitriy@izvolov.ru
Andrey Davydov
dmitriy@izvolov.ru, у меня есть безумная идея расширить const так чтобы он мог применяться к expression'у аналогично noexcept, const без параметров эквивалентен const(true). Тогда, скажем, определение оператора [] у класса vector могло бы выглядеть так:

template<typename T, bool constness>
struct add_const_if;

template<typename T>
struct add_const_if<T, true> : std::add_const<T> {};

template<typename T>
struct add_const_if<T, false>
{
using type = T;
};

template<typename T>
class vector
{
template<bool constness>
typename add_const_if<T, constness>::type& operator[] (size_t) const(constness);
};

Естественно, шаблонный аргумент constness должен автоматически выводиться при вызове. Заметим, что необходимый механизм в компиляторе уже есть. Это хорошо видно если рассмотреть свободную функцию:
template<class Vec>
typename add_const_if<typename Vec::value_type, std::is_const_v<Vec>>::type & at(Vec &, size_t);

void foo(vector<int> & xs, vector<int> const & ys)
{
at(xs, 0); // Vec -> vector<int>
at(ys, 1); // Vec -> vector<int> const
};
работает уже сейчас, а значит, и для такой функции
template<class T, bool constness>
typename add_const_if<T, constness>::type & at(vector<T> const(constness) &, size_t);
вывод шаблонных аргументов возможен.
Andrey Davydov
dmitriy@izvolov.ru
Andrey Davydov,
По-прежнему сомнительно.
Во-первых, не упомянут вариант с `&&`.
Во-вторых, помимо `const` ещё есть `volatile`.
dmitriy@izvolov.ru
ilnur.khuziev
dmitriy@izvolov.ru, ну можно сделать и volatile(bool)
ilnur.khuziev
Andrey Davydov
dmitriy@izvolov.ru,
> Во-первых, не упомянут вариант с `&&`
Можете привести пример, когда было бы полезно сделать функцию шаблонной по ref qualifier'у?
> Во-вторых, помимо `const` ещё есть `volatile`
Аналогично, не могу придумать, когда может быть полезным метод шаблонный по volatile/non-volatile?
Andrey Davydov
Олег Ляттэ
Andrey Davydov, vector::operator[] вроде бы неплохой пример и для &&, и для volatile.

Пример для &&:
std::vector<std::string> f();
auto s = f()[0]; // move-initialize s from element 0

Пример для volatile:
void f(long& s); // 1
void f(volatile long& s); // 2

volatile std::vector<long> vec;
f(vec[0]); // calls 2
Олег Ляттэ
Andrey Davydov
Олег Ляттэ, что касается &&, то для того чтобы f()[0] имело тип string &&, надо чтобы в теле `vector::operator[] (size_t i) &&` было явно написано std::move(i-й элемент). Как Вы предлагаете сделать это если operator[] будет шаблонным?
Пример же с volatile, на мой взгляд, совершенно некорректен -- то что сам объект вектора является volatile никак не связано с волатильностью его элементов.
Andrey Davydov
Олег Ляттэ
Andrey Davydov, для возврата && можно использовать static_cast - эффект будет не хуже, чем от std::move. Нужно только вывести референс нужного вида.

Квалификатор volatile имеет то же поведение, что и const. Отличается только их смысловое действие. Не даром они в стандарте часто упоминаются вместе (cv-qualifiers). Если const vector добавляет const к выдаваемым референсам на элементы, то по логике volatile vector должен делать то же самое, т.е. распространять действие своих квалификаторов на элементы.
Олег Ляттэ
smertigdon
Andrey Davydov, очень крутое предложение, сам недавно думал в точности о такой же реализации: операторы const(bool) и volatile(bool). Оформите в отдельное предложение, а то так и затеряется в комментариях.
smertigdon
Andrey Davydov
smertigdon, я не просто так написал, что это идея безумная, и как ее доработать до proposal'а я пока не представляю.
Во-первых, как было обсуждено выше, с формальной точки зрения квалификаторы const и volatile равноправны, но, по-моему, с точки зрения пользователя volatile(bool) бессмысленен. Видели ли Вы когда-нибудь функции перегруженные по volatile qualifier?
Во-вторых, с выводом шаблонного bool параметра тоже будут проблемы. К примеру, для noexcept сейчас это не работает: godbolt.org/g/6XAwy9 Clang 4.0 не компилирует, GCC 7.1 падает с internal compiler error.
В третьих, такое изменение, как мне кажется, принесет не очень много практической пользы, но очень сильно усложнит язык. Лично я, как человек которому придется поддерживать это в IDE, не очень-то этого хочу.
Если Вы напишите proposal я буду только за, но я не верю, что комитет по стандартизации примет подобное раньше 2045 года.
Andrey Davydov
smertigdon
Andrey Davydov, перегрузка по volatile(bool) может не часто используется, однако для function trait'ов, например, приходится писать по 4 перегрузки. Добавится noexcept и станет 8.
С выводом будут проблемы - почему? То, что сейчас компилятор падает, так это просто его недоработка, реальных проблем я не вижу. Хотя просто не думал об этом достаточно подробно, возможно есть подводные камни.
По поводу IDE - сейчас есть достаточно других проблем для них (всякие auto..., SFINAE, рекурсивные шаблоны, constexpr для intellisense), но сам я не разрабатывал IDE и не могу утверждать.
Чтобы дорабатывать до proposal'а нужно услышать мнение хотя бы нескольких человек, а пока что это просто беседа в комментариях :)
Я вновь вернулся к этому комментарию, так как увидел очередное предложение на эту тему: stdcpp.ru/proposals/b02ce2fb-5e59-4f15-b21c-a2cb8e25aaa0
smertigdon
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).