What's the real use case of this?
std::integral_constant
I can understand this is a wrapper with value 2:
typedef std::integral_constant<int, 2> two_t
But why not just use 2 or define a const int value with 2?
There are a few cases where std::integral_constant
is very useful.
One of them is tag dispatch. For example, std::true_type
and std::false_type
are simply std::integral_constant<bool, true>
and std::integral_constant<bool, false>
respectively. Every type trait derives from std::true_type
or std::false_type
, which enables tag dispatch:
template <typename T>
int foo_impl(T value, std::true_type) {
// Implementation for arithmetic values
}
template <typename T>
double foo_impl(T value, std::false_type) {
// Implementation for non-arithmetic values
}
template <typename T>
auto foo(T value) {
// Calls the correct implementation function, which return different types.
// foo's return type is `int` if it calls the `std::true_type` overload
// and `double` if it calls the `std::false_type` overload
return foo_impl(value, std::is_arithmetic<T>{});
}
Also, template metaprogramming libraries typically only have algorithms on lists of types rather than lists of values. If you wanted to use those algorithms for values instead, you'd have to use something like std::integral_constant
if
. However, in C++17, we get if constexpr
, which does work for this use case.
bool
regardless of whether the trait is true or not, which means that you'd only be able to have the one overload. That doesn't allow for things such as different return types or implementations which only compile if true vs. false.
2
is value, while two_t
is a type. They are two different abstractions. Each has its purpose.
2
where a type is expected. two_t
where an integral value is expected.More importantly, std::true_type
and std::false_type
are the most widely used specializations of std::integral_constant
. They are extensively used in type_traits
.
true_type
or false_type
, what would that preclude?
true_type
and false_type
are extensively used in type traits - but how is that an improvement over something like this?
Below code snippet is one way I used std::integral_constant to create an api which takes a generic value but it also checks at compile time that the value you are providing is valid or not.
#include<iostream>
struct Value {};
struct Color {};
struct Size {};
struct Point {};
enum class Property {
Color,
StrokeColor,
Opacity,
Size,
Position,
};
class Dom {
public:
// give a single api to setValue
template<Property prop, typename AnyValue>
void setValue(const std::string &obj, AnyValue value){
setValue(std::integral_constant<Property, prop>{}, obj, value);
}
private:
// specialization for each property and value type pair.
void setValue(std::integral_constant<Property, Property::Color> type,
const std::string &obj,
Color col) {std::cout<<" update color property\n";}
void setValue(std::integral_constant<Property, Property::StrokeColor> type,
const std::string &obj,
Color col){std::cout<<" update stroke color property\n";}
void setValue(std::integral_constant<Property, Property::Opacity> type,
const std::string &obj,
Value opacity){std::cout<<" update opacity property\n";}
};
int main()
{
Dom domObj;
// try to update the color property of rect1 object inside layer1
domObj.setValue<Property::Color>("layer1.rect1", Color());
// compile time error expects Color value but given Size value
//domObj.setValue<Property::Color>("layer1.rect1", Size());
return 0;
}