std::declvalstd::declval is a utility function template introduced in C++11. It's primarily used in template metaprogramming and type traits to "declare" a value of a given type, without actually creating an object. This is particularly useful when working with types that are not default-constructible or when you need to deduce the return type of a function in unevaluated contexts.
#include <iostream>
#include <type_traits>
#include <utility>
class NonDefaultConstructible {
public:
NonDefaultConstructible(int) {}
int getValue() const { return 42; }
};
int main() {
using T = decltype(std::declval<NonDefaultConstructible>().getValue());
std::cout << "The return type of getValue() is "
<< (std::is_same<T, int>::value ? "int" : "not int") << std::endl;
return 0;
}
NonDefaultConstructible that cannot be default-constructed.std::declval<NonDefaultConstructible>() to "create" a value of this type without actually constructing it.decltype(std::declval<NonDefaultConstructible>().getValue()) deduces the return type of getValue().std::is_same to check if the deduced type is int.std::declval in SFINAE#include <iostream>
#include <type_traits>
#include <utility>
template<typename T, typename = void>
struct has_toString : std::false_type {};
template<typename T>
struct has_toString<T,
std::void_t<decltype(std::declval<T>().toString())>> : std::true_type {};
struct WithToString {
std::string toString() const { return "Hello"; }
};
struct WithoutToString {
void someOtherMethod() {}
};
int main() {
std::cout << "WithToString has toString(): "
<< has_toString<WithToString>::value << std::endl;
std::cout << "WithoutToString has toString(): "
<< has_toString<WithoutToString>::value << std::endl;
return 0;
}
has_toString to check if a type has a toString() method.std::declval<T>() is used to "create" an instance of T without actually constructing it.decltype(std::declval<T>().toString()) attempts to call toString() on the "declared" value.toString() exists, the specialization with std::void_t is selected, resulting in true_type.toString() doesn't exist, SFINAE causes the primary template (false_type) to be selected.#include <iostream>
#include <type_traits>
#include <utility>
template<typename T, typename U>
auto add(T t, U u) -> decltype(std::declval<T>() + std::declval<U>()) {
return t + u;
}
int main() {
auto result1 = add(5, 3.14);
auto result2 = add(std::string("Hello, "), "World!");
std::cout << "Result 1: " << result1 << std::endl;
std::cout << "Result 2: " << result2 << std::endl;
std::cout << "Type of result1: "
<< (std::is_same<decltype(result1), double>::value ? "double" : "not double")
<< std::endl;
std::cout << "Type of result2: "
<< (std::is_same<decltype(result2), std::string>::value ? "std::string" : "not std::string")
<< std::endl;
return 0;
}
add that can work with any two types that support the + operator.decltype(std::declval<T>() + std::declval<U>()).std::declval<T>() and std::declval<U>() are used to "create" values of types T and U without actually constructing them.+ operation in an unevaluated context.std::declval is only allowed in unevaluated contexts. Attempting to use it in an evaluated context will result in a compilation error.
It's particularly useful when working with types that are not default-constructible or when you want to avoid invoking constructors.
std::declval is often used in combination with decltype and other type traits to perform compile-time type analysis and metaprogramming.
std::declval is a powerful utility in C++ template metaprogramming. It allows you to work with types in unevaluated contexts without actually creating objects of those types. This is especially useful for:
By using std::declval, you can write more flexible and powerful template code, enabling better type deduction and trait implementations. However, it's important to remember that std::declval should only be used in unevaluated contexts, typically within decltype expressions or other compile-time constructs.