是否可以参数化模板化成员函数的一致性

Is it possible to parameterize the constness of a templated member function?

本文关键字:成员 函数 一致性 参数 是否      更新时间:2023-10-16

模板使得函数签名的大部分内容都是可参数化的,除了函数名本身。但是是否也可以参数化成员函数的稳定性呢?

平凡的、极简的、非模板化的例子:

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

vs 稻草人模板假设:

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

但是是否也可以参数化成员函数的一致性呢?

不,你不能。在函数签名中不能访问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,那么

肯定会很有趣,现在我们都通过非成员friend来编写const/非const重载,我们仍然把它们当作成员来调用。

在另一个回答的评论中,你澄清了

目标是随意自定义函数的一致性,而不需要在不需要的地方重复代码

下面是一种可能性,用模板化的static成员函数表示const和非const版本的成员函数。

对于更一般的情况,需要转发参数。

两种替代方法是用非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;
}

不,但解决方法是直接的,也许更可读,意图是明确的:

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; }
};