如果 T 是函数,则不要对 T 使用 sizeof

Don't use sizeof for T if T is a function

我几乎有以下结构来检测类型是否可以按值传递:

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

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

您可以组合enable_if_t and is_function 将类型 space 分为两部分,函数和 'the rest' :

#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


}

我无法完全按照您的描述重现问题,但如果我正确理解问题,您可以使用 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;

How can it be modified so that value will contain 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 不是函数时才会被实例化。

How can it be modified so that value will contain 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、函数类型和所有其他内容都可以轻松透明地处理,不会对您的原始表达式进行任何添加。