C++17stladvanced
std::conjunction / std::disjunction / std::negation
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
Logical metafunctions that perform short-circuit AND (`conjunction`), OR (`disjunction`), and NOT (`negation`) on type traits. They short-circuit instantiation, improving compile times and enabling better error messages compared to fold expressions over traits.
Real-World Scenario
Constrain a variadic function template to accept only arguments that are all arithmetic and all nothrow-copyable.
Quick Snippet
snippet.cpp
#include <type_traits>
template<typename... Ts>
using all_arithmetic = std::conjunction<std::is_arithmetic<Ts>...>;
template<typename T>
using not_pointer = std::negation<std::is_pointer<T>>;
template<typename... Ts>
using any_floating = std::disjunction<std::is_floating_point<Ts>...>;
static_assert(all_arithmetic<int, float, double>::value);
static_assert(not_pointer<int>::value);
static_assert(any_floating<int, double, char>::value);Full Example
logical_traits.cpp
// CodeNebula - C++17 Example: std::conjunction / std::disjunction / std::negation
// Compile: g++ -std=c++17 -Wall -Wextra -o example logical_traits.cpp
// Demonstrates the C++17 logical operator traits for composing type traits.
// These short-circuit at compile time, unlike raw && / || on ::value members,
// and can be used as base classes for custom traits.
#include <iostream>
#include <type_traits>
#include <string>
// ------------------------------------------------------------------
// Custom trait: is a type a numeric scalar that fits in a register?
// ------------------------------------------------------------------
template <typename T>
using is_small_numeric = std::conjunction<
std::is_arithmetic<T>,
std::bool_constant<(sizeof(T) <= 8)>
>;
// ------------------------------------------------------------------
// Custom trait: is a type either integral or floating point?
// ------------------------------------------------------------------
template <typename T>
using is_number = std::disjunction<
std::is_integral<T>,
std::is_floating_point<T>
>;
// ------------------------------------------------------------------
// Custom trait: check ALL types in a pack are integral
// ------------------------------------------------------------------
template <typename... Ts>
using all_integral = std::conjunction<std::is_integral<Ts>...>;
// ------------------------------------------------------------------
// Custom trait: check ANY type in a pack is floating point
// ------------------------------------------------------------------
template <typename... Ts>
using any_floating = std::disjunction<std::is_floating_point<Ts>...>;
int main()
{
// ------------------------------------------------------------------
// 1. std::conjunction (logical AND, short-circuiting)
// ------------------------------------------------------------------
std::cout << "=== std::conjunction ===" << std::endl;
std::cout << " is_small_numeric<int> : "
<< is_small_numeric<int>::value << std::endl;
std::cout << " is_small_numeric<double> : "
<< is_small_numeric<double>::value << std::endl;
std::cout << " is_small_numeric<std::string>: "
<< is_small_numeric<std::string>::value << std::endl;
std::cout << " all_integral<int,short,char> : "
<< all_integral<int, short, char>::value << std::endl;
std::cout << " all_integral<int,double> : "
<< all_integral<int, double>::value << std::endl;
// ------------------------------------------------------------------
// 2. std::disjunction (logical OR, short-circuiting)
// ------------------------------------------------------------------
std::cout << "\n=== std::disjunction ===" << std::endl;
std::cout << " is_number<int> : "
<< is_number<int>::value << std::endl;
std::cout << " is_number<std::string> : "
<< is_number<std::string>::value << std::endl;
std::cout << " any_floating<int,float,char> : "
<< any_floating<int, float, char>::value << std::endl;
std::cout << " any_floating<int,short,char> : "
<< any_floating<int, short, char>::value << std::endl;
// ------------------------------------------------------------------
// 3. std::negation (logical NOT)
// ------------------------------------------------------------------
std::cout << "\n=== std::negation ===" << std::endl;
std::cout << " negation<is_integral<int>> : "
<< std::negation<std::is_integral<int>>::value << std::endl;
std::cout << " negation<is_integral<float>> : "
<< std::negation<std::is_integral<float>>::value << std::endl;
// ------------------------------------------------------------------
// 4. Combining all three
// ------------------------------------------------------------------
// is_non_bool_integer: integral AND NOT bool
using is_non_bool_int_v = std::conjunction<
std::is_integral<int>,
std::negation<std::is_same<int, bool>>
>;
using is_non_bool_bool_v = std::conjunction<
std::is_integral<bool>,
std::negation<std::is_same<bool, bool>>
>;
std::cout << "\n=== Combined ===" << std::endl;
std::cout << " non_bool_integer<int> : " << is_non_bool_int_v::value << std::endl;
std::cout << " non_bool_integer<bool> : " << is_non_bool_bool_v::value << std::endl;
return 0;
}
/*
Expected output:
=== std::conjunction ===
is_small_numeric<int> : 1
is_small_numeric<double> : 1
is_small_numeric<std::string>: 0
all_integral<int,short,char> : 1
all_integral<int,double> : 0
=== std::disjunction ===
is_number<int> : 1
is_number<std::string> : 0
any_floating<int,float,char> : 1
any_floating<int,short,char> : 0
=== std::negation ===
negation<is_integral<int>> : 0
negation<is_integral<float>> : 1
=== Combined ===
non_bool_integer<int> : 1
non_bool_integer<bool> : 0
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example logical_traits.cpp