正确的朋友模板功能的语法

时间:2017-03-09 09:53:54

标签: c++ templates friend

在C ++编程语言第四版 - 第23.4.7章朋友中,我发现了以下示例(我稍微修改了它以仅显示相关部分):

template<typename T>
class Vector {
public:
    friend Vector operator*<>(const Vector& v, int f); 
                           ^^ ~~~~ ?
};

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
    return v;
}

我尝试编译它,但是我得到了以下错误(clang):

main.cpp:8:20: error: friends can only be classes or functions
        friend Vector operator*<>(const Vector& v, int f); 
                      ^
main.cpp:8:29: error: expected ';' at end of declaration list
        friend Vector operator*<>(const Vector& v, int f); 
                               ^
                               ;
2 errors generated.

书解释说:

  

&lt;&gt;在朋友函数的名称之后需要明确朋友是模板函数。如果没有&lt;&gt;,则会假设非模板函数。

这就是全部。

没有<>此代码编译,但是当使用operator *时(例如Vector<int> v; v*12;),则会出现链接器错误:

main.cpp:(.text+0xb): undefined reference to `operator*(Vector<int> const&, int)'

所以我假设需要<>告诉编译器每次为给定类型实例化Vector模板时都应该生成operator *的函数模板。

但是我在本书的例子中做错了什么,为什么?

5 个答案:

答案 0 :(得分:7)

正如书中所说,

  需要友元函数名称之后的<>来表明朋友是模板函数。

这意味着,名称应该引用一个函数模板,该模板应该事先声明(作为模板)。 e.g。

// forward declaration of the class template
template<typename T>
class Vector;

// declaration of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);

template<typename T>
class Vector {
public:
    // friend declaration
    friend Vector operator*<>(const Vector& v, int f); 
};

// definition of the function template
template<typename T>
Vector<T> operator*(const Vector<T>& v, int f) {
    return v;
}

答案 1 :(得分:3)

在您的情况下,您 operator*直接在Vector内宣布 template<typename T> class Vector { public: template<typename> friend Vector operator*(const Vector& v, int f); }; template<typename T> Vector<T> operator*(const Vector<T>& v, int f) { return v; } ,而没有任何先前的声明。因此,正确的语法是:

fseek

live example on wandbox

答案 2 :(得分:2)

要使模板友元方法语法工作,您需要此模板方法的前向声明。

template<typename T>
class Vector;

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f);

template<typename T>
class Vector
{
    template<typename T_> friend
    Vector<T_> operator*(const Vector<T_>& v, int f);
};

template<typename T>
Vector<T> operator*(const Vector<T>& v, int f)
{
    return v;
}

答案 3 :(得分:0)

你正在使用的任何书都是错误地解释它。

您需要做的是

template<typename T>
class Vector
{
       public:
           friend Vector<T> operator*(const Vector<T>& v, int f); 
};

template<typename T>
   Vector<T> operator*(const Vector<T>& v, int f)
{
    return v;
}

上述内容使得operator*()接受Vector<T> Vector<T>的朋友但不是Vector<U>的朋友(除非TU的类型相同<T>)。

在类定义中,可以省略Vector<T> <>,但根据我的经验,仅仅是人类在说服自己函数声明和函数定义对应每个函数时会遇到更多麻烦其他。所以我一般不喜欢这样做....尽管你的电话。

在明确专门化模板时使用 template <class T> void foo(T) { /* whatever */ } template<> void foo<int> {/* something specific to int */ } 语法,但这不是您尝试做的事情。例如,使用模板化函数;

public class EventView extends AppCompatActivity {

    DatabaseReference databaseReference;
    RecyclerView recyclerView;
    int mExpandedPosition = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_view);


        databaseReference = FirebaseDatabase.getInstance().getReference().child("ApprovedEvents");
        recyclerView = (RecyclerView) findViewById(R.id.request_EventList);

        //Avoid unnecessary layout passes by setting setHasFixedSize to true
        recyclerView.setHasFixedSize(true);

        //Select the type of layout manager you would use for your recyclerView
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }

    @Override
    protected void onStart() {
        super.onStart();
        final String event_cat  = getIntent().getStringExtra("Category");

        FirebaseRecyclerAdapter<Event, RequestViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Event, RequestViewHolder>(
                Event.class,
                R.layout.events_list_row,
                RequestViewHolder.class,
                databaseReference.orderByChild("category").equalTo(event_cat)
        ) {
            @Override
            protected void populateViewHolder(RequestViewHolder viewHolder, Event model, int position) {
                viewHolder.setTitle(model.getTitle());
                viewHolder.setDesc(model.getDesc());
                viewHolder.setCategory(model.getCategory());
                viewHolder.setLocation(model.getLocation());
                viewHolder.setPrice(model.getPrice());
                viewHolder.setImageUrl(getApplicationContext(), model.getImageUrl());

                viewHolder.imageButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(EventView.this, "Image Selected", Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onBindViewHolder(RequestViewHolder viewHolder, final int position) {
                super.onBindViewHolder(viewHolder, position);

                final boolean isExpanded = position == mExpandedPosition;


                viewHolder.mView.setVisibility(isExpanded?View.VISIBLE:View.GONE);
                viewHolder.mView.setActivated(isExpanded);
                viewHolder.mView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mExpandedPosition = isExpanded ? -1:position;
                        TransitionManager.beginDelayedTransition(recyclerView);
                        notifyDataSetChanged();
                    }
                });
            }
        };
        recyclerView.setAdapter(firebaseRecyclerAdapter);
    }

    public static class RequestViewHolder extends RecyclerView.ViewHolder {
        View mView;
        ImageView imageButton;

        public RequestViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            imageButton = (ImageView) mView.findViewById(R.id.request_image);
        }

        public void setTitle(String title) {
            TextView a_title = (TextView) mView.findViewById(R.id.request_title);
            a_title.setText(title);
        }

        public void setDesc(String desc) {
            TextView a_desc = (TextView) mView.findViewById(R.id.request_desc);
            a_desc.setText(desc);
        }

        public void setLocation(String location) {
            TextView a_desc = (TextView) mView.findViewById(R.id.request_location);
            a_desc.setText(location);
        }

        public void setCategory(String category) {
            TextView a_category = (TextView) mView.findViewById(R.id.request_category);
            a_category.setText(category);
        }

        public void setPrice(String price) {
            TextView a_price = (TextView) mView.findViewById(R.id.request_price);
            a_price.setText(price);
        }

        public void setImageUrl(Context ctx, String imageUrl) {
            ImageView a_image = (ImageView) mView.findViewById(R.id.request_image);
            Picasso.with(ctx).load(imageUrl).into(a_image);
        }
    }
}

答案 4 :(得分:0)

使用 <> 也是 C++ FAQ suggests

但是您可以像往常一样简单地使用模板化声明来解决它,除了参数的命名必须与类参数不同。然后在单独的定义中,您可以再次使用任何类型名称:

template <typename T>
class Vector {
 public:
  T i{};
  // Typename must be different from the class typename(s).
  template <typename T_1>
  friend ostream& operator<<(ostream& os, const Vector<T_1>& v);
};

// Typename can be any.
template <typename T>
ostream& operator<<(ostream& os, const Vector<T>& v) {
  return os << v.i;
}

Live demo

仅此而已。不需要奇怪的 <> 中间函数声明或预声明。