C ++ 17可以推断出具有显式非类型参数的`auto`非类型`模板`参数模式匹配模板吗?

时间:2016-10-02 01:50:57

标签: c++ templates auto c++17

请考虑此示例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找不到任何结论。

2 个答案:

答案 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 ++标准。