0
#include <type_traits>

int main()
{
    std::decay<int()> p;
    static_assert(1 == sizeof(p));
    
    std::add_pointer<int> q;
    static_assert(1 == sizeof(q));
}

This is working. But why do we need instances of decay etc.? This makes typos (decay instead of decay_t etc.) harder to spot. Should these classes not have a private or deleted constructor?

not-a-user
  • 4,088
  • 3
  • 21
  • 37

2 Answers2

4

Why not? Sometimes it is necessary to create objects of traits and not allowing that would create problems for some without substantial benefit for the rest.

For example you can use traits for tag dispatch:

#include <type_traits>
#include <iostream>

void foo(std::false_type) {
    std::cout << "this is the overload for std::false_type";
}
void foo(std::true_type) {
    std::cout << "this is the overload for std::true_type";
}

int main() {
    foo( std::is_same<int,double>{} );
}

Neither caller nor the function actually use the object, but it has to be instantiated to pick the desired overload.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • 3
    I think more normal would be `std::true_type` and `std::false_type` overloads, and instantiating an `std::is_same` at the call site – Caleth Nov 13 '20 at 13:37
  • @Caleth oh right, that makes a good example. Thanks – 463035818_is_not_an_ai Nov 13 '20 at 13:44
  • In this case I would make `foo` a function template and specialize for `false_type` and `true_type`. No parameter needed. – not-a-user Nov 14 '20 at 15:21
  • @not-a-user you would, but why do you want to forbid everybody else to use tag dispatch in this case? C++ doesn't disallow stuff just because. Following the same logic one could say that we should disable passing a `bool` parameter to a function, because there is a different way to achieve same functionality – 463035818_is_not_an_ai Nov 14 '20 at 15:36
  • @not-a-user see eg [here](https://stackoverflow.com/q/58630192/4117728) for comparison of SFINAE vs tag dispatch (actually the answer uses alsmost identical example). They are complementary, sometimes one is more appropriate sometimes the other, it would be not good to disallow one – 463035818_is_not_an_ai Nov 14 '20 at 15:40
  • @not-a-user or see [here](https://en.cppreference.com/w/cpp/language/sfinae) at "attempting to create pointer to member of T, where T is not a class type" in some sense thats sfinae via tag dispatch. The example is a bit out-dated, but the principle technique is still in use – 463035818_is_not_an_ai Nov 14 '20 at 15:43
1

Why can we have instances of type traits classes in C++?

Because type traits are class templates and classes can be instantiated unless their constructors are deleted.

In fact, Cpp17UnaryTypeTrait and Cpp17BinaryTypeTrait are required / guaranteed to be Cpp17DefaultConstructible. Cpp17TransformationTrait appears to not have this requirement / guarantee.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Yes, but why? Why was it decided the the constructor shall not be deleted? Do you have a reference for the mentioned requirement/guarantee? – not-a-user Nov 14 '20 at 15:20
  • @not-a-user Its not C++ philosophy to disallow things that enable the user to make mistakes (thats different languages). The constructor would be deleted, when it would be always wrong to create an instance, but thats not the case – 463035818_is_not_an_ai Nov 14 '20 at 15:50
  • @not-a-user `Do you have a reference` see standard section [meta.rqmts]. – eerorika Nov 14 '20 at 16:15