具有第二种模板类型的部分模板特化
Partial template specialization with second template type
它是我真实问题的简化版本。为什么在第一种情况下模板专业化不起作用?
如果我交换前两个函数,它就会工作。
奇怪,但它适用于 msvc 19...
#include <string>
#include <vector>
#include <iostream>
template<typename T, typename M>
void write_impl(T &result, M m) {
for (const auto &i : result) {
write_impl(i, m);
}
}
template<typename M>
void write_impl(const std::string &result, M m) {
std::cout << result;
}
template<typename T>
void write_impl_1(T &result) {
for (const auto &i : result) {
write_impl_1(i);
}
}
template<>
void write_impl_1(const std::string &result) {
std::cout << result;
}
int main() {
std::vector<std::string> a{"42", "43", "44"};
write_impl(a, 42); // compile time error
write_impl_1(a); // works fine
}
首先,有趣的发现!花了我一点时间。
其次,函数模板不存在偏特化
完全特化是可以的,所以第二次实现会选择特化。
第一种情况是两个模板函数之间的重载决议,将选择更专业的。编译器将首先构造重载列表 - 包含调用的候选者。
就是这个东西, void write_impl(const std::string &result, M m)
不会考虑,因为还没有定义!实例化的点直接在模板的定义之后。因此,只有第一个模板在重载集中,它将匹配,因为字符串是可迭代的,并且实例化将失败,因为 char
不是。
这将引发关于 foo
的相同错误:
#include <string>
#include <vector>
#include <iostream>
template<typename T>
void bar(T &result) {
foo();
}
void foo(){}
int main() {
std::vector<std::string> a{"42", "43", "44"};
bar(a);
}
为什么第二种情况有效呢?因为编译器会“向前看”以查看所有可能的特化。但是它们仍然必须出现在实例化专业化的地方。因此,我认为第二种情况是基于 的未定义行为。我对C++的这个黑暗角落不是很精通,我在这里可能是错的。
为什么 MSCV 有效?打败我,它有时是“特殊的”,也许他们的实例化点是错误的,foo
也会错误地工作。
第一种情况不是模板特化;这是函数重载。
不起作用,因为第一个函数调用了第二个未声明的函数。如您所见,切换顺序是有效的,因为第一个(现在是第二个)知道第二个(现在是第一个)的声明(以及定义)。
不能对 C++ 中的函数进行模板偏特化;仅完全专业化。
你真的需要一种偏特化,你可以通过一个 class/struct 和它里面的一个函数。您可以部分特化 struct/class.
例如
#include <string>
#include <vector>
#include <iostream>
template <typename T, typename M>
struct foo
{
static void bar (T & result, M m)
{
for (const auto &i : result)
foo<decltype(i), M>::bar(i, m);
}
};
template <typename M>
struct foo<std::string const &, M>
{
static void bar (std::string const & result, M)
{ std::cout << result; }
};
int main()
{
std::vector<std::string> a{"42", "43", "44"};
foo<decltype(a), int>::bar(a, 42);
}
但是,如您所见,并不是很方便。
显然,如果您不需要函数模拟的部分特化,并且您对不同的和重载的模板函数感到满意(假设它们相互调用),您可以声明第一个,声明和定义第二个并定义第一个;
像
template <typename M>
void write_impl (std::string const &, M);
template<typename T, typename M>
void write_impl (T & result, M m)
{
for (const auto &i : result)
write_impl(i, m);
}
template <typename M>
void write_impl (std::string const & result, M m)
{ std::cout << result; }
它是我真实问题的简化版本。为什么在第一种情况下模板专业化不起作用?
如果我交换前两个函数,它就会工作。
奇怪,但它适用于 msvc 19...
#include <string>
#include <vector>
#include <iostream>
template<typename T, typename M>
void write_impl(T &result, M m) {
for (const auto &i : result) {
write_impl(i, m);
}
}
template<typename M>
void write_impl(const std::string &result, M m) {
std::cout << result;
}
template<typename T>
void write_impl_1(T &result) {
for (const auto &i : result) {
write_impl_1(i);
}
}
template<>
void write_impl_1(const std::string &result) {
std::cout << result;
}
int main() {
std::vector<std::string> a{"42", "43", "44"};
write_impl(a, 42); // compile time error
write_impl_1(a); // works fine
}
首先,有趣的发现!花了我一点时间。
其次,函数模板不存在偏特化
完全特化是可以的,所以第二次实现会选择特化。
第一种情况是两个模板函数之间的重载决议,将选择更专业的。编译器将首先构造重载列表 - 包含调用的候选者。
就是这个东西, void write_impl(const std::string &result, M m)
不会考虑,因为还没有定义!实例化的点直接在模板的定义之后。因此,只有第一个模板在重载集中,它将匹配,因为字符串是可迭代的,并且实例化将失败,因为 char
不是。
这将引发关于 foo
的相同错误:
#include <string>
#include <vector>
#include <iostream>
template<typename T>
void bar(T &result) {
foo();
}
void foo(){}
int main() {
std::vector<std::string> a{"42", "43", "44"};
bar(a);
}
为什么第二种情况有效呢?因为编译器会“向前看”以查看所有可能的特化。但是它们仍然必须出现在实例化专业化的地方。因此,我认为第二种情况是基于
为什么 MSCV 有效?打败我,它有时是“特殊的”,也许他们的实例化点是错误的,foo
也会错误地工作。
第一种情况不是模板特化;这是函数重载。
不起作用,因为第一个函数调用了第二个未声明的函数。如您所见,切换顺序是有效的,因为第一个(现在是第二个)知道第二个(现在是第一个)的声明(以及定义)。
不能对 C++ 中的函数进行模板偏特化;仅完全专业化。
你真的需要一种偏特化,你可以通过一个 class/struct 和它里面的一个函数。您可以部分特化 struct/class.
例如
#include <string>
#include <vector>
#include <iostream>
template <typename T, typename M>
struct foo
{
static void bar (T & result, M m)
{
for (const auto &i : result)
foo<decltype(i), M>::bar(i, m);
}
};
template <typename M>
struct foo<std::string const &, M>
{
static void bar (std::string const & result, M)
{ std::cout << result; }
};
int main()
{
std::vector<std::string> a{"42", "43", "44"};
foo<decltype(a), int>::bar(a, 42);
}
但是,如您所见,并不是很方便。
显然,如果您不需要函数模拟的部分特化,并且您对不同的和重载的模板函数感到满意(假设它们相互调用),您可以声明第一个,声明和定义第二个并定义第一个;
像
template <typename M>
void write_impl (std::string const &, M);
template<typename T, typename M>
void write_impl (T & result, M m)
{
for (const auto &i : result)
write_impl(i, m);
}
template <typename M>
void write_impl (std::string const & result, M m)
{ std::cout << result; }