选择重载函数的常量表达式?

时间:2012-12-17 20:15:15

标签: c++ c++11 metaprogramming overloading

考虑以下示例(我今天发布了几个不同的问题):

#include <iostream>
#include <vector>
#include <array>
#include <type_traits>

// Version A
template<typename T>
constexpr unsigned int f(const T&)
{
    return 1;
}

// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f(const T<T1...>&)
{
    return 2;
}

// Version C
template<typename T1, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f(const T<T1, N...>&)
{
    return 3;
}

// Main
int main(int argc, char* argv[])
{
    std::integral_constant<int, f(double())> a;
    std::integral_constant<int, f(std::vector<double>())> b;
    std::integral_constant<int, f(std::array<double, 3>())> c;
    std::cout<<a<<b<<c<<std::endl; // The goal is to return 123
    return 0;
}

此代码无法编译并返回以下编译错误:

temporary of non-literal type 'std::vector<double>' in a constant expression

如何修改此代码以进行编译?

注意:目标是将函数的第一个版本将采用的类型转换为1,将函数的第二个版本采用的类型转换为2,等等...

4 个答案:

答案 0 :(得分:1)

定义常量时不能使用临时向量:

int main(int argc, char* argv[])
{
    std::integral_constant<int, f(double())> a;
    std::vector<double> vec;
    std::integral_constant<int, f(vec)> b;
    std::integral_constant<int, f(std::array<double, 3>())> c;

    std::cout << a << b << c;
    return 0;
}

问题是编译器可能会省略vector创建,如果vector的唯一目的是传递给常量表达式函数,但实际上它不能,因为vector不是文字类型。

std::array只是c数组上面的包装器,它有简单的构造函数和析构函数。由于double也是文字类型,因此双精度数组变为字面值。

但请注意,如果您定义

struct A
{
    A(){std::cout << "I'm so complicated A!\n"; }
}

你将无法使用构造:

int main(int argc, char* argv[])
{
    std::integral_constant<int, f(A())> a;
    std::integral_constant<int, f(std::array<A, 3>())> c;

    std::cout << a << b << c;
    return 0;
}

,或者

int main(int argc, char* argv[])
{
    A a_v;
    std::integral_constant<int, f(a_v)> a;

    std::array<A, 3> c_v
    std::integral_constant<int, f(c_v)> c;

    std::cout << a << b << c;
    return 0;
}

仍然是可能的。

答案 1 :(得分:1)

你需要在函数上不可能进行部分特化,将它们包装在struct / class中完成工作:

#include <iostream>
#include <vector>
#include <array>
#include <type_traits>

// Version A                                                                                                                                                   
template<typename T>
struct f
{
  constexpr static unsigned int execute()                                                                                                                      
  {                                                                                                                                                            
    return 1;                                                                                                                                                  
  }
};

// Version B                                                                                                                                                   
template<template <typename ... > class Tpl, typename ... TplArgs>
struct f< Tpl<TplArgs...> >
{
  constexpr static unsigned int execute()                                                                                                                      
  {                                                                                                                                                            
    return 2;                                                                                                                                                  
  }
};

// Version C                                                                                                                                                   
template<template<typename, std::size_t...> class Tpl, typename FirstArg, std::size_t... N>
struct f< Tpl<FirstArg, N...> >
{
  constexpr static unsigned int execute()                                                                                                                      
  {                                                                                                                                                            
    return 3;                                                                                                                                                  
  }
};

// Main                                                                                                                                                        
int main(int argc, char* argv[])                                                                                                                               
{                                                                                                                                                              
  std::integral_constant<int, f<double>::execute()> a;                                                                                                         
  std::integral_constant<int, f<std::vector<double>>::execute()> b;                                                                                            
  std::integral_constant<int, f<std::array<double, 3>>::execute()> c;                                                                                          

  std::cout << a << ' ' << b << ' ' << c << std::endl;

  return 0;
}                                                                                                        

答案 2 :(得分:0)

您的问题是我们可以承受多少修改此代码?例如,以下编译,但你正在尝试做什么?

#include <iostream>
#include <vector>
#include <array>
#include <type_traits>

// Version A
template<typename T>
constexpr unsigned int f()
{
    return 1;
}

// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f()
{
    return 2;
}

// Version C
template<typename T1 = double, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f()
{
    return 3;
}

// Main
int main(int argc, char* argv[])
{
    std::integral_constant<int, f<double>()> a;
    std::integral_constant<int, f<std::vector<double>>()> b;
    std::integral_constant<int, f<std::array<double, 3>>()> c;
    return 0;
}

答案 3 :(得分:0)

我通过使用指针代替引用找到了解决方案:

// Version A
template<typename T>
constexpr unsigned int f(const T*)
{
    return 1;
}

// Version B
template<typename... T1, template<typename...> class T>
constexpr unsigned int f(const T<T1...>*)
{
    return 2;
}

// Version C
template<typename T1, template<typename, unsigned int...> class T, unsigned int... N>
constexpr unsigned int f(const T<T1, N...>*)
{
    return 3;
}

// Main
int main(int argc, char* argv[])
{
    std::vector<double> tmp;
    std::integral_constant<int, f(static_cast<double*>(nullptr))> a;
    std::integral_constant<int, f(static_cast<decltype(tmp)*>(nullptr))> b;
    std::integral_constant<int, f(static_cast<std::array<double, 3>*>(nullptr)) > c;
    std::cout<<a<<b<<c<<std::endl;
    return 0;
}

也许这不是最优雅的方式,但它有效。如果某人有一个优雅的相当于此,我很感兴趣。