c++ - std::string 在不应该的时候通过了 std::is_fundamental 检查 - 模板元编程

标签 c++ templates c++11 template-meta-programming typetraits

我的作业有问题。作业题目如下:

编写一个名为 Interpolate 的函数模板,它将使下面的工作正常进行。当在格式字符串中遇到相应的 % 时,将输出每个参数。所有输出最终都应使用适当的重载 << 运算符完成。\% 序列应该输出百分号。

    SomeArbitraryClass obj;
    int i = 1234;
    double x = 3.14;
    std::string str("foo");
    std::cout << Interpolate(R"(i=%, x1=%, x2=%\%, str1=%, str2=%, obj=%)", i, x, 1.001, str,  "hello", obj) << std::endl;

如果百分号的数量与要输出的参数的数量不匹配,则抛出 cs540::WrongNumberOfArgs 类型的异常。

现在,我已经开始编写代码以使其正常工作。但是,我在使用非 POD 时遇到了问题。这是我到目前为止所写的内容:

#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>

std::string Interpolate(std::string raw_string) {
    std::size_t found = raw_string.find_first_of("%");
    if(found != std::string::npos && raw_string[found-1] != '\\') {
        std::cout << "Throw cs540::ArgsMismatchException" << std::endl;
    }
    return raw_string;
}


template <typename T, typename ...Args>
std::string Interpolate(std::string raw_string, T arg_head, Args... arg_tail) {
    std::size_t found = raw_string.find_first_of("%");
    while(found != 0 && raw_string[found-1] == '\\') {
        found = raw_string.find_first_of("%", found + 1);
    }
    if(found == std::string::npos) {
        std::cout << "Throw cs540::ArgsMismatchException." << std::endl;
    }

    // Checking the typeid of the arg_head, and converting it to a string, and concatenating the strings together. 
    else {
        if(std::is_arithmetic<T>::value) { 
            raw_string = raw_string.substr(0, found) + std::to_string(arg_head) + raw_string.substr(found + 1, raw_string.size());
        }
    }
    return Interpolate(raw_string, arg_tail...);
}

int main(void) {
    int i = 24332;
    float x = 432.321;
    std::string str1("foo");
    //Works
    std::cout << Interpolate(R"(goo % goo % goo)", i, x) << std::endl;
    // Does not work, even though I'm not actually doing anything with the string argument
    std::cout << Interpolate(R"(goo %)", str1) << std::endl;
}

最佳答案

这是语义上的运行时检查。这意味着 {} 中的代码已编译,即使表达式始终为 false:

   if(std::is_arithmetic<T>::value) { 
        raw_string = raw_string.substr(0, found) + std::to_string(arg_head) + raw_string.substr(found + 1, raw_string.size());
    }

要解决这个问题,您可以这样做:

template<typename T>
void do_arithmetic( std::string& raw_string, T&& t, std::true_type /* is_arthmetic */ ) {
  raw_string = raw_string.substr(0, found) + std::to_string(std::forward<T>(t)) + raw_string.substr(found + 1, raw_string.size());
}
template<typename T>
void do_arithmetic( std::string& raw_string, T&& t, std::false_type /* is_arthmetic */ ) {
  // do nothing
}

然后输入你的代码:

do_arithmetic( raw_string, arg_head, std::is_arithmetic<T>() );

执行编译时分支。 std::is_arithmetic 的类型是 true_typefalse_type 取决于 T 是否为算​​术。这会导致调用不同的 do_arithmetic 重载。

在 C++1y 中,您可以内联执行此操作。

template<typename F, typename...Args>
void do_if(std::true_type, F&& f, Args&&... args){
  std::forward<F>(f)( std::forward<Args>(args)... );
}
template<typename...Args>
void do_if(std::false_type, Args&&...){
}
template<bool b,typename...Args>
void do_if_not(std::integral_constant<bool,b>, Args&& args){
  do_if( std::integral_constant<bool,!b>{}, std::forward<Args>(args)... );
}
template<typename C, typename F_true, typename F_false, typename...Args>
void branch( C c, F_true&&f1, F_false&& f0, Args&&... args ){
  do_if(c, std::forward<F_true>(f1), std::forward<Args>(args)... );
  do_if_not(c, std::forward<F_false>(f0), std::forward<Args>(args)... );
}

这是样板。然后我们可以在我们的函数中做:

do_if(std::is_arithmetic<T>{},
  [&](auto&& arg_head){
    raw_string = raw_string.substr(0, found) + std::to_string(arg_head) + raw_string.substr(found + 1, raw_string.size());
  },
  arg_head
);

或者,如果您想要两个分支:

branch(std::is_arithmetic<T>{},
  [&](auto&& x){
    raw_string = std::to_string(x); // blah blah
  }, [&](auto&&) {
    // else case
  },
  arg_head
);

如果 is_arithmetic 为真,第一个方法只会用 x=arg_head 实例化。

需要润色,但有点整洁。

关于c++ - std::string 在不应该的时候通过了 std::is_fundamental 检查 - 模板元编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23433854/

相关文章:

c++ - 谁能帮我让 glutBitmapString 工作?

c++ - 枚举类仅适用于 -std=c++11

c++ - OpenGL 3.3 没有渲染纹理(黑色纹理、C++、GLFW/SOIL)

c++ - C++ 中的奇怪结果

C++使模板类作为另一个类中的占位符

javascript - 了解 stache 模板中的 {{.0}} {{.1}}

c++ - std::uniform_real_distribution 使用多少个随机数?

c++ - g++ 无法推断功能映射实现的模板类型

c++ - 如何约束 3D 旋转 (Euler)

Java 表达式语言 : Interpolation?