普通模板的结束和元模板从何处开始?

che*_*erd 7 c++ templates template-meta-programming

Jörg这个问题的回答很好地描述了"正常"模板(问题所指的,可能是错误的,作为泛型),它们操作在程序上运行的数据和元模板.Jörg然后明智地提到程序数据,所以它真的是一样的.也就是说,元模板仍然是一个不同的野兽.普通模板的结束和元模板从何处开始?

我能想到的最好的测试是模板的参数是独占的class还是typename模板是"正常的",否则是元.这个测试是否正确?

MAB*_*BVT 5

边界:具有逻辑行为的签名

好吧,在我看来,边界线是绘制的,模板的签名停止为一个简单的签名,产生运行时代码,并成为显式或隐式逻辑的定义,将在编译时执行/解析.

一些例子和解释

常规模板,即仅具有typename,类或可能的值类型模板参数,一旦在编译期间实例化,就会生成可执行的cpp代码.

代码是(重要的)不在编译时执行

例如(非常简单且非常可能不切实际的例子,但解释了这个概念):

template<typename T>
T add(const T& lhs, const T& rhs) {
    return(lhs + rhs);
}

template<>
std::string add<std::string>(
                const std::string& lhs,
                const std::string& rhs) {
     return (lhs.append(rhs));
}

int main() {
    double      result = add(1.0, 2.0); // 3.0
    std::string s      = add("This is ", " the template specialization..."); 
}
Run Code Online (Sandbox Code Playgroud)

编译完成后,root-template将用于实例化double类型的上述代码,但不会执行它. 此外,specialization-template将实例化为文本连接,但也包括:不在编译时执行.

但是这个例子:

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

class INPCWithVoice {
    void doSpeak() { ; }
};

class DefaultNPCWithVoice 
    : public INPCWithVoice {
    public:
        inline std::string doSpeak() {
            return "I'm so default, it hurts... But at least I can speak...";
        }
}; 

class SpecialSnowflake
    : public INPCWithVoice {
    public:
        inline std::string doSpeak() {
            return "WEEEEEEEEEEEH~";   
        }
};

class DefaultNPCWithoutVoice {
    public:
         inline std::string doSpeak() {
            return "[...]";
        }
};

template <typename TNPC>
static inline void speak(
    typename std::enable_if<std::is_base_of<INPCWithVoice, TNPC>::value, TNPC>::type& npc) 
{
    std::cout << npc.doSpeak() << std::endl;
};

int main()
{
    DefaultNPCWithVoice    npc0 = DefaultNPCWithVoice();
    SpecialSnowflake       npc1 = SpecialSnowflake();
    DefaultNPCWithoutVoice npc2 = DefaultNPCWithoutVoice();

    speak<DefaultNPCWithVoice>(npc0);
    speak<SpecialSnowflake>(npc1);
    // speak<DefaultNPCWithoutVoice>(npc2); // Won't compile, since DefaultNPCWithoutVoice does not derive from INPCWithVoice
}
Run Code Online (Sandbox Code Playgroud)

此示例显示模板元编程(实际上是一个简单的示例...).这里发生的是,'speak'函数有一个模板化参数,如果为它传递的类型是从INPCWithVoice派生的,它将在编译时解析并衰减到TNPC.

这反过来意味着,如果没有,模板将没有实例化的候选者,并且编译已经失败.查找SFINAE获取此技术:http://eli.thegreenplace.net/2014/sfinae-and-enable_if/

此时,在编译时执行了一些逻辑,整个程序在链接到可执行文件/库后将完全解析

另一个很好的例子是:https://akrzemi1.wordpress.com/2012/03/19/meta-functions-in-c11/

在这里,您可以看到阶乘函数的模板元编程实现,证明即使字节码可以完全等于固定值使用,如果元模板衰减为常量.

最终结果示例:Fibonacci

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

template <intmax_t N>
static unsigned int fibonacci() {
    return fibonacci<N - 1>() + fibonacci<N - 2>();     
}

template <>
unsigned int fibonacci<1>() {
    return 1;   
}

template <>
unsigned int fibonacci<2>() {
    return fibonacci<1>();    
}

template <intmax_t MAX>
    static void Loop() {
    std::cout << "Fibonacci at " << MAX << ": " << fibonacci<MAX>() << std::endl;
    Loop<MAX - 1>();
}

template <>
void Loop<0>() {
    std::cout << "End" << std::endl;    
}

int main()
{
    Loop<10>();
}
Run Code Online (Sandbox Code Playgroud)

此代码仅对位于N的fibonacci序列实现标量模板参数模板元编程.此外,它显示了从10到0的循环计数的编译时!

最后

我希望这能澄清一些事情.

请记住:循环和斐波纳契示例为每个索引实例化上述模板!

因此,有一个可怕的冗余和二进制臃肿!

我自己不是专家,我确​​信在stackoverflow上有一个模板元编程功夫大师,他可以追加任何必要的信息.

  • 请避免所有大写锁定的话.它看起来像你在美国!如果你想强调使用粗体或斜体,但也不要过度使用它. (2认同)

Dan*_*our 1

尝试区分和定义术语

让我们首先尝试粗略地定义这些术语。我希望从“编程”的一个足够好的定义开始,然后反复应用meta-它的“通常”含义:

编程

编程的结果是转换一些数据的程序。

int add(int value) { return value + 42; }
Run Code Online (Sandbox Code Playgroud)

我刚刚编写了一个代码,该代码将生成一个将某些数据(整数)转换为其他数据的程序。

模板(元编程)

元编程产生将某个程序转换为另一个程序的“程序”。对于 C++ 模板,没有有形的“程序”,它是编译器行为的隐式部分。

template<typename T>
std::pair<T,T> two_of_them(T thing) {
  return std::make_pair(thing, thing);
}
Run Code Online (Sandbox Code Playgroud)

我只是编写了代码来指示编译器的行为就像一个为另一个程序发出(代码)的程序。

元模板(元元编程?)

编写元模板会产生一个“程序”,而“程序”又会产生一个程序。因此,在 C++ 中,编写会生成新模板的代码。(来自我的另一个回答:)

// map :: ([T] -> T) -> (T -> T) -> ([T] -> T)
//         "List"       "Mapping"   result "type" (also a "List")
// --------------------------------------------------------
template<template<typename...> class List,
         template<typename> class Mapping>
struct map {
  template<typename... Elements>
  using type = List<typename Mapping<Elements>::type...>;
};
Run Code Online (Sandbox Code Playgroud)

这是编译器如何将两个给定模板转换为新模板的描述。

可能的反对意见

看看其他答案,人们可​​能会说我的元编程示例不是“真正的”元编程,而是“通用编程”,因为它没有在“元”级别实现任何逻辑。但是,给出的编程示例可以被认为是“真正的”编程吗?它也不实现任何逻辑,它是从数据到数据的简单映射,就像元编程示例实现从代码(auto p = two_of_them(42);)到代码(模板“填充”了正确的类型)的简单映射一样。

因此,在我看来,添加条件(例如通过专门化)只会使模板变得更加复杂,但不会改变其本质。

你的测试

绝对没有。考虑:

template<typename X>
struct foo {
  template<typename Y>
  using type = X;
};
Run Code Online (Sandbox Code Playgroud)

foo是一个具有单个typename参数的模板,但是模板中的“结果”(命名foo::type......只是为了一致性)“结果” - 无论给出什么参数 - 给定的类型foo(以及行为,程序)由该类型实现)。