Можно ли параметризовать константу шаблонной функции-члена?
Шаблоны делают большую часть информации о параметрической подписи параметрируемой отдельно от имени функции. Но можно ли параметризовать константу функции-члена?
Тривиальный, минималистский, не шаблонный пример:
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; }
};