在C ++ 11中的模板化函数中处理void变量 [英] Handling a void variable in a templatized function in C++11

查看:95
本文介绍了在C ++ 11中的模板化函数中处理void变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个模板类,必须在调用参数和返回类型通用的函数之前执行一些操作.

I have a template class that must perform some operation before calling a function whose parameters and return type are generic.

这是方法:

template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
  // prepare for call
  // ...
  ReturnType rv = makeCall(args...);  // [1]
  // dismiss the call
  // ...
  return rv;
}

当然,当 ReturnType 不是 void 时,它可以正确编译.当我在这种情况下使用它时:

Of course it's compiling correctly when ReturnType is not void. When I use it in this context:

function<void>(firstArg, secondArg);

编译器以

错误:函数返回"void" [-fpermissive]

指向标有[1]的行.

除了将 -fpermissive 传递给编译器之外,还有其他解决方案吗?我希望有一个独特的方法,因为我发现的可能解决方案是使用 enable_if is_same 实例化不同的版本.

Is there any solution other than passing -fpermissive to the compiler? I would prefer to have a unique method, because I possible solution I found is to instantiate different versions using enable_if and is_same.

谢谢.

-更新-

这是一个完整的示例.我应该说我们的函数确实是类方法.

This is a complete example. I should have said that our functions are indeed class methods.

#include <type_traits>
#include <iostream>

class Caller {
public:
    Caller() {}

    template <typename ReturnType, typename ...Arguments>
    ReturnType call(Arguments ... args) {
        prepare();

        ReturnType rv = callImpl<ReturnType>(args...);

        done();

        return rv;
    }

private:
    void prepare() {
        std::cout << "Prepare\n";
    }

    void done() {
        std::cout << "Done\n";
    }

    template <typename ReturnType, typename ...Arguments>
    typename std::enable_if<std::is_same<ReturnType, void>::value, ReturnType>::type callImpl ( Arguments ... args) {
        std::cout << "Calling with void\n";
        return;
    }

    template <typename ReturnType, typename ...Arguments>
    typename std::enable_if<std::is_same<ReturnType, bool>::value, ReturnType>::type callImpl (Arguments ... args) {
        std::cout << "Calling with bool\n";
        return true;
    }

    template <typename ReturnType, typename ...Arguments>
    typename std::enable_if<std::is_same<ReturnType, int>::value, ReturnType>::type callImpl (Arguments ... args) {
        std::cout << "Calling with int\n";
        return 42;
    }
};


int main(int argc, char *argv[]) {

    Caller c;
    auto rbool = c.call<bool> (1,20);
    std::cout << "Return: " << rbool << "\n";
    auto rint = c.call<int> (1,20);
    std::cout << "Return: " << rint << "\n";

    // the next line fails compilation. compile with --std=c++11
    c.call<void>("abababa");

    return 0;
}

-更新-

没什么大问题:使用 std :: bind(& Caller :: callImpl< ReturnType> ;,这个,args).

推荐答案

这是我尝试的一种通用的C ++ 11兼容解决方案,您可以轻松地重用.

Here's my attempt at a general C++11-compliant solution that you can easily reuse.

让我们从创建一个简单的 type trait 开始,该特征将 void 转换为空结构.这不会引入任何代码重复.

Let's start by creating a simple type trait that converts void to an empty struct. This doesn't introduce any code repetition.

struct nothing { };

template <typename T>
struct void_to_nothing 
{
    using type = T;
};

template <>
struct void_to_nothing<void>
{
    using type = nothing;
};

template <typename T>
using void_to_nothing_t = typename void_to_nothing<T>::type; 

我们还需要一种方法来调用任意函数,将最终的 void 返回类型转换为 nothing :

We also need a way to call an arbitrary function converting an eventual void return type to nothing:

template <typename TReturn>
struct helper
{
    template <typename TF, typename... Ts>
    TReturn operator()(TF&& f, Ts&&... xs) const
    {
        return std::forward<TF>(f)(std::forward<Ts>(xs)...);
    }
};

template <>
struct helper<void>
{
    template <typename TF, typename... Ts>
    nothing operator()(TF&& f, Ts&&... xs) const
    {
        std::forward<TF>(f)(std::forward<Ts>(xs)...);
        return nothing{};
    }
};

template <typename TF, typename... Ts>
auto with_void_to_nothing(TF&& f, Ts&&... xs)
    -> void_to_nothing_t<
           decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...))>
{
    using return_type = 
        decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...));

    return helper<return_type>{}(std::forward<TF>(f), std::forward<Ts>(xs)...);
}

用法:

template <typename ReturnType, typename ...Args>
void_to_nothing_t<ReturnType> function (Args ...args) {
  // prepare for call
  // ...
  auto rv = with_void_to_nothing(makeCall, args...);  // [1]
  // dismiss the call
  // ...
  return rv;
}

实时魔杖示例

马特·卡拉布雷斯(Matt Calabrese)提出的一项提案,称为"<常规>无效" ,可以解决此问题.您可以在此处找到:"P0146R1" .

There's a proposal by Matt Calabrese called "Regular Void" that would solve this issue. You can find it here: "P0146R1".

这篇关于在C ++ 11中的模板化函数中处理void变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆