在C++ 11中处理模板化函数中的void变量

Hap*_*tus 5 c++ templates variadic c++11

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

这是方法:

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

当然,如果ReturnType没有正确编译它void.当我在这种情况下使用它时:

function<void>(firstArg, secondArg);
Run Code Online (Sandbox Code Playgroud)

编译器响应

error: return-statement with a value, in function returning 'void' [-fpermissive]

指向标有[1]的行.

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

先感谢您.

- 更新 -

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

- 更新 -

不是一个大问题:使用std::bind(&Caller::callImpl<ReturnType>, this, args).

Vit*_*meo 5

以下是我可以轻松重用的符合C++ 11标准的通用解决方案.

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

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; 
Run Code Online (Sandbox Code Playgroud)

我们还需要一种方法来调用将最终void返回类型转换为的任意函数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)...);
}
Run Code Online (Sandbox Code Playgroud)

用法:

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;
}
Run Code Online (Sandbox Code Playgroud)

直播wandbox示例


Matt Calabrese提出了一项名为"Regular Void"的提案,可以解决这个问题.你可以在这里找到它:"P0146R1".

  • 遗留问题是`function`不再返回`void`,而是'nothing`.这暴露了内部实现细节. (2认同)

R S*_*ahu 5

取决于您希望在线条中完成的任务

// dismiss the call
Run Code Online (Sandbox Code Playgroud)

你可以使用:

template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
  // prepare for call
  // ...

  CallDismisser c;
  return  makeCall(args...);  // [1]
}
Run Code Online (Sandbox Code Playgroud)

只要析构函数CallDismisser可以执行您需要执行的所有操作,这将起作用.