如果T是函数,则不要将sizeof用于T.

时间:2017-01-05 17:15:22

标签: c++ templates c++14 function-pointers

我接近跟随struct检测是否可以通过值传递类型:

template <class T>
struct should_be_passed_by_value {
    static constexpr bool value = 
        std::is_scalar<T>::value || 
        std::is_array<T>::value || 
        std::is_reference<T>::value || 
        (sizeof(T) <= sizeof(void*));
};

问题是:当我为类C函数指针或std :: function实例化它时,编译器说:

invalid application of 'sizeof' to a function type

(当然)。

如何修改以便value包含false

4 个答案:

答案 0 :(得分:9)

  

如何修改以使值包含false?

任何问题都可以通过额外的间接层来解决。我们已经内置了一些这些。基本上,您希望仅在T不是函数时才使用小号检查。已经存在一个元函数:std::conditional。我们可以用它来推迟评估。

小的检查,我们分成了自己的元函数:

template <class T>
struct is_small
    : std::integral_constant<bool, (sizeof(T) <= sizeof(void*))>
{ };

然后我们可以将您的条件重写为:

template <class T>
struct should_be_passed_by_value {
    static constexpr bool value = 
        std::is_scalar<T>::value || 
        std::is_array<T>::value || 
        std::is_reference<T>::value || 
        std::conditional_t<
            std::is_function<T>::value,
            std::false_type,
            is_small<T>>::value;
};

这样,is_small<T>仅在T不是函数时才被实例化。

答案 1 :(得分:4)

  

如何修改以使值包含false?

它遵循should_be_passed_by_value的可能实现(实际上是一个最小的工作示例):

#include<type_traits>
#include<functional>

template <class T, typename = void>
struct should_be_passed_by_value: std::false_type {};

template <class T>
struct should_be_passed_by_value
<T, std::enable_if_t<
    (std::is_scalar<T>::value ||
    std::is_array<T>::value ||
    std::is_reference<T>::value || 
    (sizeof(T) <= sizeof(void*)))
>>: std::true_type {};

void f() {}

int main() {
    static_assert(should_be_passed_by_value<int>::value, "!");
    static_assert(should_be_passed_by_value<char>::value, "!");
    static_assert(not should_be_passed_by_value<std::function<void(void)>>::value, "!");
    static_assert(not should_be_passed_by_value<void(void)>::value, "!");
    static_assert(should_be_passed_by_value<void(*)(void)>::value, "!");
}

基本思想是依靠部分专业化 而且,您实际上不必定义自己的value数据成员。由于您使用的是C ++ 14,should_be_passed_by_value可以直接从std::false_typestd::true_type继承。

默认情况下,您的类型T不应按值传递(should_be_passed_by_value继承自std::false_type)。
如果T未通过所有检查,则由于std::enable_if_t的工作原理而放弃专门化。因此,主要模板被选中,这意味着T不应该通过值传递 如果T通过了所有检查,则std::enable_if_tvoid,并且专业化优先于主要模板。请注意,专门化继承自std::true_type,这意味着在这种情况下T应该按值传递。

从示例中可以看出,std::function,函数类型和所有其他内容都可以轻松透明地处理,而不会添加原始表达式。

答案 2 :(得分:2)

我无法像您描述的那样完全重现问题,但如果我正确理解了问题,您可以使用template specialization来彻底解决此问题。以下示例使用Visual Studio 2015和gcc 4.9进行编译。

#include <type_traits>

// Non-function types
template <class T>
struct should_be_passed_by_value 
{
    static constexpr bool value = 
        std::is_scalar<T>::value || 
        std::is_array<T>::value || 
        std::is_reference<T>::value || 
        (sizeof(T) <= sizeof(void*));
};

// Function type
template <class Return, class ... Args>
struct should_be_passed_by_value<Return(Args...)> 
{
    static constexpr bool value = false; // What value for functions?
};

以下是编译

的一些用例
// All of these use cases compile
#include <array>
const auto u = should_be_passed_by_value<std::array<int, 10>>::value;
const auto v = should_be_passed_by_value<int*()>::value;
const auto w = should_be_passed_by_value<int()>::value;
const auto x = should_be_passed_by_value<int(int)>::value;
const auto y = should_be_passed_by_value<int*>::value;
const auto z = should_be_passed_by_value<int>::value;

答案 3 :(得分:0)

部分答案(正如评论中提到的那样,并不清楚std :: function对你有什么用处,它应该是这样的)

您可以将enable_if_tis_function结合起来,将类型空间划分为两个部分,即函数和其他部分&#39; :

#include <type_traits>
#include <functional>
#include <iostream>

template <class T, class Enable = void>
struct should_be_passed_by_value; // primary case that we will never hit

template <class T>
struct should_be_passed_by_value<T, typename std::enable_if_t<std::is_function<T>::value>>
{
  static constexpr bool value = false; // case 0
};

template <class T>
struct should_be_passed_by_value<T, typename std::enable_if_t<!std::is_function<T>::value>>
{
  static constexpr bool value =
      std::is_scalar<T>::value || // case 1
      std::is_array<T>::value || // case 2
      std::is_reference<T>::value || // case 3
      (sizeof(T) <= sizeof(void *)); /// case 4
};

void testF(){};

int main()
{
  std::function<void()> f;
  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<decltype(testF)>::value << std::endl; // result 0, case 0

  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<decltype(5)>::value << std::endl; // res 1, case 1

  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<char[42]>::value << std::endl; // res 1, case 2

  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<int&>::value << std::endl; // res 1 , case 3

  struct Small {char _{2};};
  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<Small>::value << std::endl; // res 1, case 4

  struct Big {char _[16];};
  std::cout << "should_be_passed_by_value1 " << should_be_passed_by_value<Big>::value << std::endl; // res 0, case 4


}