2012-06-26 20 views
6

我在写一个与std::function(或者至少这些类在很多方面类似)共享几个不同特性的类。大家都知道std::function通过指定模板参数(例如std::function<void (std::string&)>)来实例化,这对我的班级来说是一样的。我有一个例外,但如果返回值是无效的(std::function<"return value" ("parameters">),我想专门在我的课程中使用单个函数函数。我需要在编译时完成这个工作,而且我不能像它应该那样工作。下面是解释一些测试代码:编译时的类型特化

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 
     if(std::is_void<Ret>::value) 
     { 
      // Do something... 
     } 

     else /* Not a void function */ 
     { 
      Ret returnVal; 
      return returnVal; 
     } 
    } 
}; 

int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
} 

正如你可以清楚地看到,如果编译器不会在上面的测试中去掉了“其他”的分支,我的代码将尝试创建一个空值(即void returnVal;) 。问题是,所以我结束了一个编译器错误编译器不会删除分支:

./test.cpp: In instantiation of ‘Ret Test::operator()(Args ...) [with Ret = void; Args = {char}]’: ./test.cpp:27:10: required from here ./test.cpp:18:8: error: variable or field ‘returnVal’ declared void ./test.cpp:19:11: error: return-statement with a value, in function returning 'void' [-fpermissive]

人们通常会使用std::enable_ifstd::is_void相结合,问题是,我不希望专注于功能模板,但在模板。

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    typename std::enable_if<!std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     Ret returnVal; 
     return returnVal; 
    } 

    typename std::enable_if<std::is_void<Ret>::value, Ret>::type 
    Ret operator()(Args...) 
    { 
     // It's a void function 
     // ... 
    } 
}; 

如果我使用上面的代码,而不是我最终甚至更多的错误,并没有解决

./test.cpp:11:2: error: expected ‘;’ at end of member declaration 
./test.cpp:11:2: error: declaration of ‘typename std::enable_if<(! std::is_void<_Tp>::value), Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:11:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:2: error: expected ‘;’ at end of member declaration 
./test.cpp:18:2: error: declaration of ‘typename std::enable_if<std::is_void<_Tp>::value, Ret>::type Test<Ret(Args ...)>::Ret’ 
./test.cpp:6:11: error: shadows template parm ‘class Ret’ 
./test.cpp:18:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive] 
./test.cpp:18:6: error: ‘int Test<Ret(Args ...)>::operator()(Args ...)’ cannot be overloaded 
./test.cpp:11:6: error: with ‘int Test<Ret(Args ...)>::operator()(Args ...)’ 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...)’: 
./test.cpp:22:2: warning: no return statement in function returning non-void [-Wreturn-type] 
./test.cpp: In instantiation of ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:28:10: required from here 
./test.cpp:13:7: error: variable or field ‘returnVal’ declared void 
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’: 
./test.cpp:15:2: warning: control reaches end of non-void function [-Wreturn-type] 

对不起,如果我只是普通的愚蠢,答案是显而易见的。我对模板相当陌生,在任何其他线索/问题中找不到合适的答案。

+0

有一个建议,包括一个“静态如果”,可以做你想要在你的第一个例子。 – mfontanini

+0

@mfontanini:这两个提案中的任何一个实际上是否都会向前推进是另一回事。他们有缺陷,并且一些东西变得比他们看起来复杂得多,如果你开始使用带有静态模板的SFINAE-if –

+0

“问题是编译器没有移除分支,所以最终出现编译器错误”=“ >不幸的是,编译器*不需要*,这(我承认)有点不安。 –

回答

7

有几件事情从您的描述中不完全清楚,所以我将从最一般的答案开始。

假设模板有其他功能,必须保持其行为相同,并且您只想重新定义该特定功能的行为,最简单的答案是将模板分成两部分,并使用继承进行合并他们。在这一点上,你可以在基本模板使用模板偏特:

template <typename T, typename... Args> 
struct tmpl_base { 
    T operator()(Args... args) { 
     //generic 
    } 
}; 
template <typename... Args> 
struct tmpl_base<void,Args...> { 
    void operator()(Args... args) { 
    } 
}; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> : tmp_base<Ret,Args...> { 
    // do not declare/define operator(), maybe bring the definition into scope: 
    using tmp_base<Ret,Args...>::operator(); 

    // Rest of the class 

如果这是你的模板的唯一功能,那么部分专业化是一个不需要滥用继承更简单的解决方案。

+0

原则上,我会建议在组合作品(或者这里,委托给一个免费函数)时使用继承,但是我不得不承认,我可能不会拿出任何如此简单的东西。 –

+0

@MatthieuM。:这可能是继承的唯一滥用,我认为这是一种必然的罪恶​​。我不认为在这种情况下,你可以在这里做任何的委托,因为主要的问题是'返回委托(args ...)'表达式对于返回void的情况将是不合格的。这个特定的问题阻止委托给一个组合对象或一个不同的功能。 –

+0

我想我们会这样做。很难过这么简单的任务需要多少代码。我希望C++有[静态如果](http://dlang.org/version.html#StaticIfCondition)的:( –

2

一种解决方案是部分专门化您的班级模板。

#include <iostream> 
#include <type_traits> 

template <typename T> class Test { }; 

template <typename Ret, typename... Args> 
class Test<Ret (Args...)> 
{ 
public: 
    Ret operator()(Args...) 
    { 

     std::cout<<"non-void function"<<std::endl; 
     Ret returnVal; 
     return returnVal; 

    } 
}; 

template <typename... Args> 
class Test<void (Args...)> 
{ 
public: 
    void operator()(Args...) 
    { 

     std::cout<<"void function"<<std::endl; 
    } 
}; 


int main(int argc, char * argv[]) 
{ 
    Test<void (char)> test; 
    test('k'); 
}