请考虑此示例(also available on wandbox):
template <template <auto> class>
void test() { }
template <int>
struct X { };
尝试在test<X>()
4.0 {em>(trunk)上实例化clang++
会导致编译错误:
error: no matching function for call to 'test'
test<X>();
^~~~~~~
note: candidate template ignored:
invalid explicitly-specified argument for 1st template parameter
void test() { }
我最初的假设/直觉是test
可用于匹配任何具有非类型参数的template
。
但是,以下代码段已成功编译:
template <template <auto> class>
void test() { }
// vvvv
template <auto>
struct X { };
这是打算吗?在P0127R2找不到任何结论。
答案 0 :(得分:9)
这绝对是有意的。模板模板参数只能匹配采用相同类型参数的模板。这样:
template <template <auto> class>
void test() { }
只能使用可以采用任何类型的非类型参数的类模板进行实例化。但是这个:
template <int>
struct X { };
不是这样的类模板。 X
只能使用int
进行实例化。它只是与模板模板参数的规范不匹配,因此错误。如果test
想要用指针类型实例化其类模板怎么办?或指向函数或指向成员的指针?那是不可能的。
您的第二次尝试template <auto> struct X { };
与模板模板参数匹配,因此格式正确。另请注意,相反,test
采用template <int> class
参数并传入template <auto> struct X { };
也是格式正确的,因为参数比参数更通用。
相关措辞在[temp.arg.template]中:
template-argument 匹配模板 template-parameter
P
当 template-parameter-list 中的每个模板参数 of template-argument 的相应类模板或别名模板A
匹配P
的 template-parameter-list 中的相应模板参数。两个模板参数匹配,如果它们属于同一类型(类型,非类型,模板),对于非类型模板参数,它们的类型是 等效(14.5.6.1),对于模板模板参数,每个相应的模板参数都是递归匹配的。
注意:等效措辞接受auto
- auto
个案并拒绝auto
- int
个案,但似乎也拒绝了int
- auto
案例(基于我的阅读)。我将尝试对其进行澄清。
答案 1 :(得分:2)
除了Barry的回答,这让我很好奇,以下是使用Clang 4.0(SVN)的四种可能的组合和结果,see also on wandbox:
package main
import (
"encoding/csv"
"flag"
"fmt"
"io"
"os"
"strings"
"time"
)
func routine(r []string) {
processData(r)
ch <- r
}
var ch = make(chan []string)
func main() {
start := time.Now()
flag.Parse()
fmt.Print(strings.Join(flag.Args(), "\n"))
if *filename == "REQUIRED" {
return
}
csvfile, err := os.Open(*filename)
if err != nil {
fmt.Println(err)
return
}
defer csvfile.Close()
reader := csv.NewReader(csvfile)
i := 0
for {
record, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
fmt.Println(err)
return
}
i++
go routine(record)
fmt.Printf("go %d %s\n", i, record)
}
for ; i >= 0; i-- {
fmt.Printf("<- %d %s\n", i, <-ch)
}
fmt.Printf("\n%2fs", time.Since(start).Seconds())
}
func processData([]string) {
time.Sleep(10 * time.Millisecond)
}
var filename = flag.String("f", "REQUIRED", "source CSV file")
var numChannels = flag.Int("c", 4, "num of parallel channels")
//var bufferedChannels = flag.Bool("b", false, "enable buffered channels")
从那以后,#1和#2显然是完全匹配并且按预期工作。 #3将在模板上调用bool实现,该模板不仅可以处理bool而且可以处理所有类型,而#4将尝试调用期望通用对象(auto)的定义,其中对象仅提供可能的子集(bool)
模板化函数template <bool> struct obj_bool { }; // object taking a value of boolean type
template <auto> struct obj_auto { }; // object taking a value of deduced type
// ^^^^^^ Note: this is a template value argument (non-type template argument)
template <template <auto> typename> void fn_auto() { }
template <template <bool> typename> void fn_bool() { }
// ^^^^^^^^^^^^^^^^^^^^^^^^ Note: this is a template type argument
// ^^^^^^ taking a template value argument
int main() {
fn_bool<obj_bool>(); // #1 bool->bool OK (exact match)
fn_auto<obj_auto>(); // #2 auto->auto OK (exact match)
fn_bool<obj_auto>(); // #3 bool->auto OK (sub-set)
//fn_auto<obj_bool>(); // #4 auto->bool Error: no matching function.
}
承诺对采用任何值类型(自动)的模板进行可能的实例化。因此只给它一个可能性的子集(bool)违反了这个承诺。
虽然不是很明显,但限制是有道理的。抱歉我的措辞不符合C ++标准。