質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.45%

  • C++

    4553questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    2442questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • VC++

    163questions

    VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

  • Visual C++

    150questions

    Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

  • コンパイルエラー

    41questions

    コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

関数テンプレートの完全特殊化のコンパイルエラー

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 2,672

JADEN

score 97

コンパイルエラーになる原因を教えてください。
コンパイラー: VC++ 2015

// main.cpp
#include "sub.h"
#include <iostream>
#include <string>

int main() {
    std::string string0("test");
    std::cout << GetLength(string0) << std::endl;

    const char* string1 = "test";
    std::cout << GetLength(string1) << std::endl;
}
// sub.h
#ifndef SUB_H
#define SUB_H

#include <string>

template <typename type>
std::size_t GetLength(const type& string) {
    return string.length();
}

template <>
std::size_t GetLength<char*>(const char*& string) {
    return std::strlen(string);
}

#endif
エラーメッセージ

エラー (アクティブ)        指定された型と一致する 関数テンプレート "GetLength" のインスタンスはありません
エラー    C2912    明示的な特殊化 'size_t GetLength<char*>(const char *&)' は関数テンプレートの特殊化ではありません
エラー    C2912    明示的な特殊化 'size_t GetLength<char*>(const char *&)' は関数テンプレートの特殊化ではありません
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+6

こんにちは。

一見不思議に見えますが、実はconstのかかり具合が一致してません。
const type&と書くとconsttypeにかかります。近い方にかかります。
const char*&と書くとconstchar*ではなくcharにかかります。近い方にかかるからです。
const type&typechar*に置き換えると、コンパイラ的にはconst (char*)&(*1)となってますが、ソースの記述は(const char)* &になっていて矛盾しているため、特殊化として認識されません。
(*1)実際に書くとエラーになります。

そこで、yohhoyさんが書かれているようにすれば通ります。
constは左、右どちらにおいてもよいので下記のように考えると分かりやすいと思います。
(個人的にはconstは右においた方が間違いを減らせると考えてます。)

template <typename type>
std::size_t GetLength(type const& str) {
    return str.length();
}

template <>
std::size_t GetLength<const char*>(const char* const& str) {
    return std::strlen(str);
}

でも、このケースであれば、関数テンプレートをやめて暗黙の型変換を利用して下記のように書いても動作します。

std::size_t GetLength(const std::string& str) {
    return str.length();
}


std::string型はchar const*を受け取れるので、string1は暗黙の型変換でstd::string型の一時オブジェクトへ変換されます。そして、const参照は一時オブジェクトを受け取れます。


ところで、型名と判別し辛い変数名は使わないことを強くお薦めします。
机上デバッグが辛いですから。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/11 21:59

    回答ありがとうございます。

    >> ところで、型名と判別し辛い変数名は使わないことを強くお薦めします。

    型は英語でtypeなので、typeとしました。
    他に良い名前があれば教えてください。

    キャンセル

  • 2016/02/11 22:12

    各GetLength()引数名のstringの件です。std::stringと紛らわしいと思います。

    キャンセル

+6

template <typename type>
std::size_t GetLength(const type& string) {
    return string.length();
}

このコードのconst type& stringconsttype&を修飾しています。

template <>
std::size_t GetLength<char*>(const char*& string) {
    return std::strlen(string);
}

こちらのコードのconst char*& stringconstchar*を修飾しています。つまり、typeに相当する部分がconst char*となり、<char*>ともconst type&とも一致しないため、テンプレートの特殊化ではないというエラーになるのです。

コンパイルを通すためにはchar* const& stringのように書く必要があります。

ただし、その場合、const char*を引数で渡すと上の関数テンプレートと一致してしまい、別のエラーになります。そのため、const char* const& stringという特殊化も用意する必要があり、冗長なコードになってしまいます。

C++11からは、もうちょっとスマートに書けるようになっています。

template <typename type, class = typename std::enable_if<std::is_class<type>::value>::type>
std::size_t GetLength(const type& string) {
    return string.length();
}

std::size_t GetLength(const char* string) {
    return std::strlen(string);
}

テンプレート引数が長くなっていますが、typeがクラスの時だけ一致させる仕組みです。

下はテンプレートの特殊化ではなく通常の関数のオーバーロードにしています。テンプレートの特殊化だとコードが冗長になることがあるので、通常のオーバーロードで書くことが多いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/11 21:50 編集

    回答ありがとうございます。

    そもそも、完全特殊化の実装をsub.hに書いており、アップしたコードには書いてないですが、main.cpp以外のソースファイルからもsub.hをインクルードしていたため、二重定義になっていたようです。
    普通の関数・クラステンプレートは、ヘッダファイルに実装を書くため、同じ書き方だと思い込んでいました。

    キャンセル

  • 2016/02/11 21:55 編集

    >>下はテンプレートの特殊化ではなく通常の関数のオーバーロードにしています。テンプレートの特殊化だとコードが冗長になることがあるので、通常のオーバーロードで書くことが多いです。

    コードが冗長になることを除き、完全特殊化とオーバーロードの違いがない様に思えるのですが、何か違いがあるのでしょうか。

    キャンセル

  • 2016/02/11 22:38

    > 何か違いがあるのでしょうか。

    同じです。同じ結果になるなら見やすい方がいいという理屈です。
    ただ、コンパイラが呼ぶべき関数を特定する際、先にオーバーロードの関数から一致する型があるかどうか探して、なければテンプレートの特殊化を探し、それでもなければテンプレートに当てはめる、ということをやっているので、優先順という違いはあります。

    キャンセル

+2

下記なら通るでしょうか?

template <>
std::size_t GetLength<const char*>(const char* const& string) {
  return std::strlen(string);
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/11 01:50

    私の回答の方でも触れていますが、それだと`char *`を受け付けてくれません。ですからconstなし版も書かないとエラーになってしまいます。

    キャンセル

  • 2016/02/11 07:54

    そうですね。この例では無理に特殊化するのではなく、関数オーバーロードの方が適切かと思います。

    キャンセル

  • 2016/02/11 22:00

    回答ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C++

    4553questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    2442questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • VC++

    163questions

    VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

  • Visual C++

    150questions

    Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

  • コンパイルエラー

    41questions

    コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。