将基于 constexpr 的 C++17 模板化代码转换为 C++14
Convert if constexpr based C++17 templatized code to C++14
我正在将一个用 C++ 17 编写的项目降级到 C++ 14。降级时,我遇到了一段涉及 if constexpr
的代码,我希望将其转换为 C++ 14(据我所知, if constexpr
是 C++ 17 的特性)。
Boost 的 is_detected
用于检查给定类型是否具有星号运算符或 get 方法。
#include <iostream>
#include <boost/type_traits/is_detected.hpp>
#include <type_traits>
#include <boost/optional/optional.hpp>
#include <memory>
#include <typeinfo>
template < template < typename... > typename Operation, typename... Args >
constexpr bool is_detected_v = boost::is_detected< Operation, Args... >::value;
template < typename T >
using has_star_operator = decltype( *std::declval< T >( ) );
template < typename T >
using has_get_method = decltype( std::declval< T >( ).get( ) );
有一个函数调用 deref
用于取消引用指针、数组、迭代器、智能指针等类型
template < typename T >
inline constexpr const auto&
deref( const T& value )
{
if constexpr ( is_detected_v< has_star_operator, T > )
{
return deref( *value );
}
else if constexpr ( is_detected_v< has_get_method, T > )
{
return deref( value.get( ) );
}
else
{
return value;
}
}
我试图通过使用 std::enable_if
来形成一个没有 if constexpr
的解决方案,如下所示:
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && is_detected_v<has_star_operator, T>,
decltype( *std::declval< const T >( ) )>::type
deref(const T& value)
{
std::cout << "STAR " << typeid(*value).name() << std::endl;
return *value;
}
template <typename T>
typename std::enable_if<
is_detected_v<has_get_method, T>,
decltype( std::declval< const T >( ).get( ) ) >::type
deref(const T& value)
{
std::cout << "GET " << typeid(value.get()).name() << std::endl;
return value.get();
}
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && !is_detected_v<has_star_operator, T>,
const T>::type
deref(const T& value)
{
std::cout << "NONE\n";
return value;
}
int main()
{
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int *b = &a;
const int array[ 4 ] = {VALUE, 0, 0, 0};
//const auto list = {std::make_unique< int >( VALUE ), std::make_unique< int >( 0 ),
// std::make_unique< int >( 0 )};
//const auto iterator = list.begin( );
//std::unique_ptr<int> u = std::make_unique< int >( VALUE );
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
//std::cout << deref(iterator) << std::endl;
//std::cout << deref(u) << std::endl;
}
但是,对于迭代器和智能指针等必须进行多次取消引用的情况,上述方法失败了。例如,对于 std::unique_ptr
,将首先调用 p.get()
(auto q = p.get()
),然后调用星号运算符 (*q
)。
我是模板初学者,需要一些帮助。请告诉我如何解决这个问题。
我正在使用 GCC 5.4 编译。
利用标签分派的解决方案怎么样?
想法是将代码从您的分支移动到三个辅助函数。
这些函数在最后一个参数上重载,其唯一目的是
允许您稍后调用正确的:
template <typename T>
constexpr const auto& deref(const T& value);
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 0>) {
return deref(*value);
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 1>) {
return deref(value.get());
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 2>) {
return value;
}
template <typename T>
constexpr const auto& deref(const T& value) {
using dispatch_t = std::integral_constant<
int, is_detected_v<has_star_operator, T>
? 0
: (is_detected_v<has_get_method, T> ? 1 : 2)>;
return deref(value, dispatch_t{});
}
通过上面的实现,编译如下:
int main() {
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int* b = &a;
const int array[4] = {VALUE, 0, 0, 0};
const auto list = {std::make_unique<int>(VALUE),
std::make_unique<int>(0), std::make_unique<int>(0)};
const auto iterator = list.begin();
std::unique_ptr<int> u = std::make_unique<int>(VALUE);
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
std::cout << deref(iterator) << std::endl;
std::cout << deref(u) << std::endl;
}
并输出:
42
42
42
42
42
42
另请注意,在 C++14 之前,当声明本身就是模板的模板参数时,语法是
template <template <typename...> class Operation, typename... Args>
// ^ class: you can use typename since C++17
constexpr bool is_detected_v = boost::is_detected<Operation, Args...>::value;
添加到@paolo 提供的“标签分发”解决方案。
我想要一个可以用于多种情况的通用解决方案,所以我尝试使用模板来实现它。它似乎适用于大多数情况。
template<int N, class T, template<typename...> class... V>
struct dispatch_constant;
template<int N, class T>
struct dispatch_constant<N, T>
{
static constexpr int value = N;
};
template<int N, typename T, template<typename...> class U, template<typename...> class... V>
struct dispatch_constant<N, T, U, V...>
{
static constexpr int value = is_detected_v<U, T> ? N - sizeof...(V) - 1 : dispatch_constant<N, T, V...>::value;
};
完整代码如下:
#include <iostream>
#include <boost/type_traits/is_detected.hpp>
#include <type_traits>
#include <boost/optional/optional.hpp>
#include <memory>
#include <typeinfo>
#include <initializer_list>
template < template < typename... > class Operation, typename... Args >
constexpr bool is_detected_v = boost::is_detected< Operation, Args... >::value;
template < typename T >
using has_star_operator = decltype( *std::declval< T >( ) );
template < typename T >
using has_get_method = decltype( std::declval< T >( ).get( ) );
template <typename T>
constexpr const auto& deref(const T& value);
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 0>) {
std::cout << "STAR " << typeid(T).name() << std::endl;
return deref(*value);
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 1>) {
std::cout << "GET " << typeid(T).name() << std::endl;
return deref(value.get());
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 2>) {
std::cout << "NONE " << typeid(T).name() << std::endl;
return value;
}
template<int N, class T, template<typename...> class... V>
struct dispatch_constant;
template<int N, class T>
struct dispatch_constant<N, T>
{
static constexpr int value = N;
};
template<int N, typename T, template<typename...> class U, template<typename...> class... V>
struct dispatch_constant<N, T, U, V...>
{
static constexpr int value = is_detected_v<U, T> ? N - sizeof...(V) - 1 : dispatch_constant<N, T, V...>::value;
};
template <typename T>
constexpr const auto& deref(const T& value) {
// using dispatch_t = std::integral_constant<
// int, is_detected_v<has_star_operator, T>
// ? 0
// : (is_detected_v<has_get_method, T> ? 1 : 2)>;
// std::cout << typeid(T).name() << " " << typeid(dispatch_t).name() << std::endl;
using dispatch_t = std::integral_constant< int, dispatch_constant<2, T, has_star_operator, has_get_method>::value >;
return deref(value, dispatch_t{});
}
int main() {
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int* b = &a;
const int array[4] = {VALUE, 0, 0, 0};
std::initializer_list< std::unique_ptr<int> > list = {std::make_unique<int>(VALUE),
std::make_unique<int>(0), std::make_unique<int>(0)};
const auto iterator = list.begin();
std::unique_ptr<int> u = std::make_unique<int>(VALUE);
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
std::cout << deref(iterator) << std::endl;
std::cout << deref(u) << std::endl;
}
我正在将一个用 C++ 17 编写的项目降级到 C++ 14。降级时,我遇到了一段涉及 if constexpr
的代码,我希望将其转换为 C++ 14(据我所知, if constexpr
是 C++ 17 的特性)。
Boost 的 is_detected
用于检查给定类型是否具有星号运算符或 get 方法。
#include <iostream>
#include <boost/type_traits/is_detected.hpp>
#include <type_traits>
#include <boost/optional/optional.hpp>
#include <memory>
#include <typeinfo>
template < template < typename... > typename Operation, typename... Args >
constexpr bool is_detected_v = boost::is_detected< Operation, Args... >::value;
template < typename T >
using has_star_operator = decltype( *std::declval< T >( ) );
template < typename T >
using has_get_method = decltype( std::declval< T >( ).get( ) );
有一个函数调用 deref
用于取消引用指针、数组、迭代器、智能指针等类型
template < typename T >
inline constexpr const auto&
deref( const T& value )
{
if constexpr ( is_detected_v< has_star_operator, T > )
{
return deref( *value );
}
else if constexpr ( is_detected_v< has_get_method, T > )
{
return deref( value.get( ) );
}
else
{
return value;
}
}
我试图通过使用 std::enable_if
来形成一个没有 if constexpr
的解决方案,如下所示:
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && is_detected_v<has_star_operator, T>,
decltype( *std::declval< const T >( ) )>::type
deref(const T& value)
{
std::cout << "STAR " << typeid(*value).name() << std::endl;
return *value;
}
template <typename T>
typename std::enable_if<
is_detected_v<has_get_method, T>,
decltype( std::declval< const T >( ).get( ) ) >::type
deref(const T& value)
{
std::cout << "GET " << typeid(value.get()).name() << std::endl;
return value.get();
}
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && !is_detected_v<has_star_operator, T>,
const T>::type
deref(const T& value)
{
std::cout << "NONE\n";
return value;
}
int main()
{
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int *b = &a;
const int array[ 4 ] = {VALUE, 0, 0, 0};
//const auto list = {std::make_unique< int >( VALUE ), std::make_unique< int >( 0 ),
// std::make_unique< int >( 0 )};
//const auto iterator = list.begin( );
//std::unique_ptr<int> u = std::make_unique< int >( VALUE );
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
//std::cout << deref(iterator) << std::endl;
//std::cout << deref(u) << std::endl;
}
但是,对于迭代器和智能指针等必须进行多次取消引用的情况,上述方法失败了。例如,对于 std::unique_ptr
,将首先调用 p.get()
(auto q = p.get()
),然后调用星号运算符 (*q
)。
我是模板初学者,需要一些帮助。请告诉我如何解决这个问题。
我正在使用 GCC 5.4 编译。
利用标签分派的解决方案怎么样?
想法是将代码从您的分支移动到三个辅助函数。 这些函数在最后一个参数上重载,其唯一目的是 允许您稍后调用正确的:
template <typename T>
constexpr const auto& deref(const T& value);
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 0>) {
return deref(*value);
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 1>) {
return deref(value.get());
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 2>) {
return value;
}
template <typename T>
constexpr const auto& deref(const T& value) {
using dispatch_t = std::integral_constant<
int, is_detected_v<has_star_operator, T>
? 0
: (is_detected_v<has_get_method, T> ? 1 : 2)>;
return deref(value, dispatch_t{});
}
通过上面的实现,编译如下:
int main() {
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int* b = &a;
const int array[4] = {VALUE, 0, 0, 0};
const auto list = {std::make_unique<int>(VALUE),
std::make_unique<int>(0), std::make_unique<int>(0)};
const auto iterator = list.begin();
std::unique_ptr<int> u = std::make_unique<int>(VALUE);
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
std::cout << deref(iterator) << std::endl;
std::cout << deref(u) << std::endl;
}
并输出:
42
42
42
42
42
42
另请注意,在 C++14 之前,当声明本身就是模板的模板参数时,语法是
template <template <typename...> class Operation, typename... Args>
// ^ class: you can use typename since C++17
constexpr bool is_detected_v = boost::is_detected<Operation, Args...>::value;
添加到@paolo 提供的“标签分发”解决方案。
我想要一个可以用于多种情况的通用解决方案,所以我尝试使用模板来实现它。它似乎适用于大多数情况。
template<int N, class T, template<typename...> class... V>
struct dispatch_constant;
template<int N, class T>
struct dispatch_constant<N, T>
{
static constexpr int value = N;
};
template<int N, typename T, template<typename...> class U, template<typename...> class... V>
struct dispatch_constant<N, T, U, V...>
{
static constexpr int value = is_detected_v<U, T> ? N - sizeof...(V) - 1 : dispatch_constant<N, T, V...>::value;
};
完整代码如下:
#include <iostream>
#include <boost/type_traits/is_detected.hpp>
#include <type_traits>
#include <boost/optional/optional.hpp>
#include <memory>
#include <typeinfo>
#include <initializer_list>
template < template < typename... > class Operation, typename... Args >
constexpr bool is_detected_v = boost::is_detected< Operation, Args... >::value;
template < typename T >
using has_star_operator = decltype( *std::declval< T >( ) );
template < typename T >
using has_get_method = decltype( std::declval< T >( ).get( ) );
template <typename T>
constexpr const auto& deref(const T& value);
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 0>) {
std::cout << "STAR " << typeid(T).name() << std::endl;
return deref(*value);
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 1>) {
std::cout << "GET " << typeid(T).name() << std::endl;
return deref(value.get());
}
template <typename T>
constexpr const auto& deref(const T& value, std::integral_constant<int, 2>) {
std::cout << "NONE " << typeid(T).name() << std::endl;
return value;
}
template<int N, class T, template<typename...> class... V>
struct dispatch_constant;
template<int N, class T>
struct dispatch_constant<N, T>
{
static constexpr int value = N;
};
template<int N, typename T, template<typename...> class U, template<typename...> class... V>
struct dispatch_constant<N, T, U, V...>
{
static constexpr int value = is_detected_v<U, T> ? N - sizeof...(V) - 1 : dispatch_constant<N, T, V...>::value;
};
template <typename T>
constexpr const auto& deref(const T& value) {
// using dispatch_t = std::integral_constant<
// int, is_detected_v<has_star_operator, T>
// ? 0
// : (is_detected_v<has_get_method, T> ? 1 : 2)>;
// std::cout << typeid(T).name() << " " << typeid(dispatch_t).name() << std::endl;
using dispatch_t = std::integral_constant< int, dispatch_constant<2, T, has_star_operator, has_get_method>::value >;
return deref(value, dispatch_t{});
}
int main() {
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int* b = &a;
const int array[4] = {VALUE, 0, 0, 0};
std::initializer_list< std::unique_ptr<int> > list = {std::make_unique<int>(VALUE),
std::make_unique<int>(0), std::make_unique<int>(0)};
const auto iterator = list.begin();
std::unique_ptr<int> u = std::make_unique<int>(VALUE);
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
std::cout << deref(iterator) << std::endl;
std::cout << deref(u) << std::endl;
}