5
\$\begingroup\$

Here is a type trait which - I feel - could fit in std next to std::add_const and std::remove_const:

#include <type_traits>

template <typename T>
struct set_constness_of { 
    template <typename U>
    struct by {
        using type = typename std::conditional<
            std::is_const<U>::value,
            typename std::add_const<T>::type,
            typename std::remove_const<T>::type
            >::type;
    };
};

Note this is intentionally C++11 to minimize requirements for using it. One can obviously define set_constness_of_t in which by is a type rather than a struct with a type.

I've also written a tiny sample program (coliru.com) to ensure it runs, at least for some cases.

/* the above definition of set_constness_of goes here (paste it) */
#inlcude <iostream>  

struct foo {
    void m() { std::cout << "Non-const\n"; }
    void m() const { std::cout << "Const\n"; }
};

template <class T> void call_m() { T().m(); }

int main() {
    call_m<foo>();
    using bar = const int;
    call_m< set_constness_of<foo>::by<bar>::type >();
}

Questions:

  • Is this correctly defined for all cases?
  • What do you think about placing the second template parameter as an inner struct's template param, rather than having two template parameters on the outer struct?
  • What do you think about the choice of naming?

Other comments are welcome.

\$\endgroup\$
3
\$\begingroup\$

I don't see any obvious omissions or bugs.

The naming and inner struct are neatly expressive (and remind me of some testing frameworks' precondition/assertion chaining). It's not something that's done by the standard library, but I don't think it should be hugely controversial.

I might go with a naming like copy_const<foo>::from<bar> - or perhaps even the other way around: copy_const<bar>::to<foo>. Or even both! (Actually, I now wish std::is_assignable worked like that - it would be easier to remember which parameter is which!)

None of the above is a concrete suggestion - intended merely as food for thought!

\$\endgroup\$
  • \$\begingroup\$ If find the second version very fluent! Or maybe something like constness_from<foo>::to<bar>. There's a lot of valid option. A strawpoll might be nice :p \$\endgroup\$ – Calak Nov 21 '18 at 19:23
  • \$\begingroup\$ copy_const sounds like "copy constructor"... \$\endgroup\$ – einpoklum Nov 21 '18 at 21:58
  • \$\begingroup\$ @Calak: See my answer. \$\endgroup\$ – einpoklum Nov 21 '18 at 22:13
3
\$\begingroup\$

Following @Calak's comment, I'm now thinking of the following change of order of the template parameters:

#include <type_traits>

template <typename U>
struct constness_of { 
    enum { value = std::is_const<U>::value };
    template <typename T>
    struct applied_to {
        using type = typename std::conditional<
            value,
            typename std::add_const<T>::type,
            typename std::remove_const<T>::type
            >::type;
    };
#if __cplusplus >= 201402L
    template <typename U>
    using applied_to_t = typename applied_to<U>::type;
#endif
};

Which would be used as follows:

if (constness_of<bar>::value) { std::cout << "bar is const!\n"; }

and:

using altered_foo = constness_of<bar>::applied_to<foo>::type;
using altered_foo_2 = constness_of<bar>::applied_to_t<foo>;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.