Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Templates make most things about a function signature parameterizable apart from the function name itself. But is it also possible to parameterize the constness of a member function?

Trivial, minimalist, non-templated example:

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

vs straw-man templated hypothetical:

struct Foo {
    template<typename T> T self() std::constness_of(T) { return this; }
};
like image 780
Catskul Avatar asked Oct 18 '16 19:10

Catskul


People also ask

Can a non templated class have a templated member function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.

Can member functions be declared as template?

Member functions can be function templates in several contexts. All functions of class templates are generic but are not referred to as member templates or member function templates. If these member functions take their own template arguments, they are considered to be member function templates.

What is a templated function?

Function templates. Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.

What is a non type template parameter?

A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type. A pointer or reference to a class object.


Video Answer


2 Answers

But is it also possible to parameterize the constness of a member function?

No, you cannot. You don't have access in the function signature to the implicit object to which this points, so you can't dispatch on it or template on it in any way. cv-qualifiers on member functions have to be spelled out.

For more complicated member functions, you could have one invoke the other (typically the non-const invoking the const one to avoid UB) to avoid some code duplication.


Or you could always write a non-member 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; }
};

We need the SFINAE to ensure that self() isn't found for unexpected types like Wrapper<Foo>. Note that this is quite a big longer than your original code, so really only makes sense in the context of having complicated logic.

Would sure be amusing if UFCS was adopted and now we all write our const/non-const overloads via non-member friends that we still invoke as if they were members.

like image 129
Barry Avatar answered Nov 15 '22 23:11

Barry


In a comment on another answer you clarified that

the goal is to customize constness of the function at will without requiring duplicate code where it's otherwise not needed

The following is one possibility, expressing both const and non-const versions of a member function in terms of a templated static member function.

For the more general case one needs to forward arguments.

Two alternatives are to express the const member function in terms of the non-const member function, or vice versa. But as I recall that involves a bit of ugly casting. Or some ugliness, not sure (sorry, I'm now sitting on a very limited internet connection).

#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;
}
like image 30
Cheers and hth. - Alf Avatar answered Nov 15 '22 23:11

Cheers and hth. - Alf