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

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

ただいまの
回答率

87.49%

SFINAE が効かない.....??

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 420

score 23

質問概要

SFINAEを使って、テンプレートクラスのメンバ関数のオーバーロードの適用を分岐させようとしています。
しかし、SFINAEが効いて欲しいところでなぜか効いてくれず、コンパイルエラーになってしまいます....

質問詳細

※ 各クラス・構造体はft名前空間に定義しています。

SFINAEのための構造体を以下のように定義しています。

    template <bool B, typename T>
    class enable_if
    {
        public:
        typedef T type;
    };


    template <typename T>
    class enable_if<false, T>
    {};

    template <typename T, typename U>
    struct is_same
    {
        static const bool value = false;
    };

    template <typename T>
    struct is_same <T, T>
    {
        static const bool value = true;
    };

    template <class Iterator>
    struct iterator_traits
    {

        typedef typename Iterator::value_type            value_type;
        typedef typename Iterator::difference_type        difference_type;
        typedef typename Iterator::pointer                pointer;
        typedef typename Iterator::reference            reference;
        typedef typename Iterator::iterator_category    iterator_category;
    };

これらのSFINAEを使って、以下ように関数のオーバーロードをしようとしています。

template <class T>
    class list
    {
        ~~~~~~~~~~~~~~~~~~
        /*Iterator assign*/

        template <class InputIterator> \
        void                     assign(
        typename ft::enable_if<ft::is_same<typename ft::iterator_traits<InputIterator>::value_type, value_type>::value, InputIterator>::type first,
        InputIterator last);

        /*Fill assign*/
        void                    assign(size_type n, const value_type& val);
       ~~~~~~~~~~~~~~~~~~~

}


int main()
{
  ft::list<int> lst;
  lst.assign((size_t)5, 42);
}

上記のようにmain 関数を書いてコンパイルします。

% g++ -std=c++98 main.cpp

所望の挙動

まず、Iterator assign がコンパイル可能か見に行って、今回はtypedef typename Iterator::value_type         value_type;が定義できないことから、ここでSFINAEが効いて Fill assign を見にいくことでコンパイル成功となって欲しいです。

実際の挙動

しかし、結果は、以下のようなエラー文が出て、コンパイル失敗してしまいます。

./../SFINAE.hpp:39:20: error: type 'int' cannot be used prior to '::' because it has no members
                typedef typename Iterator::value_type                   value_type;
                                 ^
./list.hpp:167:51: note: in instantiation of template class 'ft::iterator_traits<int>' requested here
                typename ft::enable_if<ft::is_same<typename ft::iterator_traits<InputIterator>::value_type, value_type>::value, InputIterator>::type first,
                                                                ^
main.cpp:39:9: note: while substituting deduced template arguments into function template 'assign' [with InputIterator = int]
    lst.assign((size_t)5, 42);

要は、 Iterator assign がコンパイルできない段階でなぜかコンパイルエラー確定になってしまっているんです。
どうして、SFINAMEの機能が効かないのでしょうか... 
調べても結論が出ませんでしたので質問させていただきます。よろしくお願いします。

listクラス全体

もし必要だったら....

namespace ft
{
    template <class T>
    class list
    {
        class list_const_iterator;
        class node
        {
            public:
            node* right;
            node* left;
            T *data;

            node() : right(this), left(this), data(NULL) {return ;};
            node(node *f, node* b, T d): right(f), left(b), data(new T(d)){ return ;};
            node(const T& val) : data(new T(val)){};
            void    operator=(node x)
            {
                this->data = x.data;
                this->left = x.left;
                this->right = x.right;
            }
        };

        class list_iterator
        {
            public:
            friend class list_const_iterator;
            typedef T            value_type;
            typedef ptrdiff_t    difference_type;
            typedef T*            pointer;
            typedef T&            reference;
            typedef std::iterator<std::bidirectional_iterator_tag, T> iterator_category;


            node _node;
            list_iterator(){};
            list_iterator(const node& other) : _node(other){};
            list_iterator(list_const_iterator other) : _node(other._node){};
            ~list_iterator(){};

            reference operator*();
            pointer operator->();
            list_iterator &operator++();
            list_iterator operator++(int);
            list_iterator &operator--();
            list_iterator operator--(int);
            bool operator==(const list_iterator &other) const;
            bool operator!=(const list_iterator &other) const;

            list_iterator operator=(const list_iterator &other)
            {
                this->_node = other._node;
                return (*this);
            }

        };

        class list_const_iterator
        {

            public:
            friend class list_iterator;
            typedef T                    value_type;
            typedef ptrdiff_t            difference_type;
            typedef const T*            pointer;
            typedef const T&            reference;
            typedef std::bidirectional_iterator_tag iterator_category;


            node _node;
            list_const_iterator(){};
            list_const_iterator(const node& other) : _node(other){};
            list_const_iterator(const list_iterator& other) : _node(other._node) {}
            ~list_const_iterator(){};


            reference operator*();
            pointer operator->();
            list_const_iterator &operator++();
            list_const_iterator operator++(int);
            list_const_iterator &operator--();
            list_const_iterator operator--(int);


            bool operator==(const list_const_iterator &other) const;
            bool operator!=(const list_const_iterator &other) const;
        };


        public:
        typedef T                                                                value_type;
        typedef allocator<T>                                                    Allocator;
        typedef Allocator                                                        allocator_type;
        typedef typename allocator_type::reference                                reference;
        typedef typename allocator_type::const_reference                        const_reference;
        typedef typename allocator_type::pointer                                pointer;
        typedef typename allocator_type::const_pointer                            const_pointer;
        typedef typename allocator_type::size_type                                size_type;
        typedef list_iterator                                                    iterator;
        typedef list_const_iterator                                                const_iterator;
        typedef ft::l_reverse_iterator<iterator>                                    reverse_iterator;
        typedef ft::l_reverse_iterator<const_iterator>                            const_reverse_iterator;
        typedef ptrdiff_t                                                        difference_type;

        /*
            Constructor
        */
        explicit list (const allocator_type& alloc = allocator_type());
        explicit list (size_type n, const value_type& val = value_type(),
                    const allocator_type& alloc = allocator_type());

        template <class InputIterator>
        list (
            typename ft::enable_if<ft::is_same<typename ft::iterator_traits<InputIterator>::value_type, typename off_const<value_type>::value_type>::value, InputIterator>::type first,
            InputIterator last,
            const allocator_type& alloc = allocator_type());
        list (const list& x);

        /*
            Destructor
        */
        ~list(){};

        /*
            Iterators
        */
        iterator                begin();
        const_iterator            begin() const;
        iterator                end();
        const_iterator            end() const;
        reverse_iterator        rbegin();
        const_reverse_iterator    rbegin() const;
        reverse_iterator        rend();
        const_reverse_iterator    rend() const;
        /*
            Capacity
        */
        size_type                size() const;
        bool                    empty() const;
        size_type                max_size() const;
        /*
            Element access
        */
        reference                front();
        const_reference            front() const;
        reference                back();
        const_reference            back() const;
        /*
            Modifiers
        */


        template <class InputIterator> \
        void                     assign(
        typename ft::enable_if<ft::is_same<typename ft::iterator_traits<InputIterator>::value_type, value_type>::value, InputIterator>::type first,
        InputIterator last);
        void                    assign(size_type n, const value_type& val);

        iterator                insert(iterator position, const value_type& val);
        void                    insert(iterator position, size_type n, const value_type& val);
        template<class InputIterator>
        void                    insert(
                iterator position,
                typename ft::enable_if<ft::is_same<typename ft::iterator_traits<InputIterator>::value_type, value_type>::value, InputIterator>::type first,
                InputIterator last);


        iterator                erase(iterator position);
        iterator                erase(iterator first, iterator last);

        void                    push_back(const value_type& val);
        void                    pop_back();
        void                    push_front(const value_type& val);
        void                    pop_front();
        void                    swap(list &x);
        void                    resize (size_type n, value_type val = value_type());
        void                    clear();

        /*
            Operations
        */

        void                    splice (iterator position, list& x);
        void                    splice (iterator position, list& x, iterator i);
        void                    splice (iterator position, list& x, iterator first, iterator last);
        void                    remove (const value_type& val);
        template <class Predicate>
        void                    remove_if (Predicate pred);
        void                    unique();
        template <class BinaryPredicate>
        void                    unique (BinaryPredicate binary_pred);

        void                    merge (list& x);
        template <class Compare>
        void                    merge (list& x, Compare comp);
        void                    sort();
        template <class Compare>
        void                    sort(Compare comp);

    private:
        node* _list; //head
        size_type _size;
        /*
            Private Functions
        */
        void make_sequence(typename list<T>::node *, typename list<T>::node *);


    };


} // namespace ft
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • yohhoy

    2021/03/03 17:06

    GCCオプションに -std=c++98 を指定されていますが、C++98準拠は要件ですか?(C++11も使えない?)

    キャンセル

回答 1

checkベストアンサー

+1

SFINAE の基本原則は

テンプレートの展開に失敗したならばその候補は除外される

です。

質問者の例では ft::iterator_traits の展開には成功した上で展開結果がエラーなので、 ft::is_same や ft::enable_if に渡る以前にエラーが確定してしまっている状態であると考えられます。


よく見たら違うかも。

依存名は推論できないです。 std::enable_if は返却値の型のところに書くのが基本ですね。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/03/09 15:10

    ありがとうございます! std::enable_if を書き換えて、いくつかモジュールを追加してうまくいきました!

    キャンセル

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

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

関連した質問

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