Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use cases for std::add_const and similar

Some type transformations in <type_traits> can also be expressed using core language syntax (e.g. std::add_const<T>::type is/seems equivalent to const T). Dtto for std::add_lvalue_reference, and perhaps others. What is the use for these type traits?

I fully understand the standard would be providing an "incomplete toolbox" without them, and I can imagine use in a meta way, something like this:

template<typename In, template <typename> class Modifier>
struct Apply {
  typedef typename Modifier<T>::type Out;
};

Apply<int, std::add_const>

Are there any other use cases for these traits which can be expressed syntactically, or are they just included "out of a sense of completeness" and for the occasional meta-use?

like image 335
Angew is no longer proud of SO Avatar asked May 08 '13 07:05

Angew is no longer proud of SO


4 Answers

Those traits come from Boost and the proposal to add them to the standard, N1345, quotes Andrei Alexandrescu as saying:

"I understand the symmetry argument for adding add_const, add_volatile, add_cv, and add_pointer, however I would argue in favor of eliminating them. The language-provided equivalents are just simpler and nicer."

The same proposal also gives this rationale:

Author's Note: superficially the add_const, add_volatile and add_cv classes are irrelevant, since, for example, add_const::type is the same as T const, for all T (currently this does not apply to function types - but issue 295 addresses this). However the experience from boost is that several users have asked for these templates to be present in the library for the following reasons: (a) Some users find these more explicit - when combining transformation templates in particular, users like the kind of "built in documentation" that these templates provide. (b) Not all users are aware that cv-qualifying a reference is allowed and has no effect, or that cv-qualifying a type that is already cv-qualified is allowed and has no effect. (c) Compilers may emit warnings when cv-qualifying a type that is a reference, or already has a cv-qualifier, these templates can be implemented such that these messages are suppressed in these cases.

Also, for add_reference (renamed to add_lvalue_reference in the standard):

Author's Note: the add_reference template was one of the original motivations behind the boost type traits library. However the resolution to issue 106 makes the template appear largely redundant. In spite of that add_reference may well be useful in suppressing compiler warnings when inadvertently creating references to references in template code.

like image 197
Jonathan Wakely Avatar answered Nov 15 '22 07:11

Jonathan Wakely


These traits are provided for occasional meta-use. It makes it possible to transport wanted cv-qualifiers around in meta programming.

template<class T,template<class> class Trait>
struct transform
{
  /* working with T creating newT*/

  typedef Trait<newT>::type type;
};

template<class T>
struct special_transform
  : transfrom<T, std::add_const>
{};

In this case you could not replace std::add_const with const.

like image 8
Jan Herrmann Avatar answered Nov 15 '22 08:11

Jan Herrmann


add_const can be used to resolve type deduction conflicts.

template <typename T>
class wrapper;

template <typename T>
bool operator==(wrapper<T> const& w, T const& t);

Problems arise if we use wrapper<T const>:

wrapper<int const> w = { 42 };
assert(w == 42); // Error: conflicting deduced types

T is simultaneously deduced to be both int and int const. This can be resolved using add_const:

template <typename T>
bool operator==(wrapper<T> const& w, add_const_t<T>& t);
like image 7
Joseph Thomson Avatar answered Nov 15 '22 08:11

Joseph Thomson


The only use case I know is illustrated below:

struct F
{
    bool f() const { return true; }
    bool f() { return false; }
};
assert(!F{}.f())
assert(std::add_const_t< F >{}.f());

It also needed for testing of cv-ref-qualified member-functions functionality, that may differs for different overloadings (only for lref-qualified modern C++ has handy std::as_const function):

struct F
{
    int g() & { return 1; }
    int g() const & { return 2; }
    int g() && { return 3; }
    int g() const && { return 4; }
};
F f;
assert(f.g() == 1);
assert(std::as_const(f).g() == 2);
assert(F{}.g() == 3);
assert(std::add_const_t< F >{}.g() == 4); // rarely needed, but if needed, then it helps
like image 1
Tomilov Anatoliy Avatar answered Nov 15 '22 08:11

Tomilov Anatoliy