Можно ли параметризовать константу шаблонной функции-члена?

Шаблоны делают большую часть информации о параметрической подписи параметрируемой отдельно от имени функции. Но можно ли параметризовать константу функции-члена?

Тривиальный, минималистский, не шаблонный пример:

struct Foo {
    Foo *       self()       { return this; }
    Foo const * self() const { return this; }
};

vs straw-man шаблонный гипотетический:

struct Foo {
    template<typename T> T self() std::constness_of(T) { return this; }
};

Ответ 1

Но возможно ли также параметризовать константу функции-члена?

Нет, вы не можете. У вас нет доступа к сигнатуре функции к неявному объекту, к которому относятся this, поэтому вы не можете отправлять его или шаблон на нем каким-либо образом. cv-квалификаторы по функциям участника должны быть указаны.

Для более сложных функций-членов вы можете вызвать вызов другого (обычно не const, вызывающего const, чтобы избежать UB), чтобы избежать дублирования кода.


Или вы всегда можете написать не-член friend:

struct Foo {
    template <class T,
        std::enable_if_t<std::is_base_of<Foo, std::decay_t<T>>::value>* = nullptr
        >
    friend T* self(T& x) { return &x; }
};

Нам нужен SFINAE, чтобы убедиться, что self() не найден для неожиданных типов, таких как Wrapper<Foo>. Обратите внимание, что это намного больше, чем ваш исходный код, поэтому на самом деле это имеет смысл только в контексте сложной логики.

Было бы забавно, если бы UFCS был принят, и теперь все мы пишем наши перегрузки const/non const через не-член friend, который мы все еще вызываем, как если бы они были членами.

Ответ 2

В комментарий на другой ответ вы пояснили, что

" цель состоит в том, чтобы настроить константу функции по желанию, не требуя дублирования кода, где это иначе не требуется

Ниже приведена одна возможность, выражающая версии const и non const функции-члена в терминах функции-шаблона static.

Для более общего случая нужно перенаправить аргументы.

Две альтернативы - это выразить функцию члена const в терминах функции-члена не const или наоборот. Но, как я помню, это связано с уродством. Или какое-то уродство, не уверен (извините, я сейчас сижу на очень ограниченном интернет-соединении).

#include <string>

//--------------------------------------- Machinery:

template< class Guide, class Result >
struct With_const_like_t_
{
    using T = Result;
};

template< class Guide, class Result >
struct With_const_like_t_<Guide const, Result>
{
    using T = Result const;
};

template< class Guide, class Result >
using With_const_like_ = typename With_const_like_t_<Guide, Result>::T;


//--------------------------------------- Example usage:

class Bork
{
private:
    std::string s_  = "42";

    template< class This_class >
    static auto foo_impl( This_class& o )
        -> With_const_like_<This_class, std::string>&
    { return o.s_; }

public:
    auto foo()
        -> decltype( foo_impl( *this ) )
    { return foo_impl( *this ); }

    auto foo() const
        -> decltype( foo_impl( *this ) )
    { return foo_impl( *this ); }
};

#include <iostream>
#include <typeinfo>
using namespace std;

auto main()
    -> int
{
    Bork v;
    Bork const c;
    v.foo() = "Hi there!";
    #ifdef TEST
        c.foo() = "This assignment to `const` won't compile.";
    #endif
    cout << v.foo() << endl;
}

Ответ 3

Нет, но обход прямой, и, возможно, более читаемый, намерение ясно:

struct Foo {
    template<typename T>
    std::enable_if_t<std::is_const<T>::value,T> self() const { return this; }
    template<typename T>
    std::enable_if_t<!std::is_const<T>::value,T> self() { return this; }
};